aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/blog/2025-06-13-cargo-culture-shock/cargo-culture-shock.md332
1 files changed, 332 insertions, 0 deletions
diff --git a/src/blog/2025-06-13-cargo-culture-shock/cargo-culture-shock.md b/src/blog/2025-06-13-cargo-culture-shock/cargo-culture-shock.md
new file mode 100644
index 0000000..e17f89d
--- /dev/null
+++ b/src/blog/2025-06-13-cargo-culture-shock/cargo-culture-shock.md
@@ -0,0 +1,332 @@
1# Cargo culture shock 🦀
2
3After a long adventure
4[porting my cube solver to the web](../2025-06-06-webdev), I decided to
5try out something completely different, like learning a new language.
6Rust is one on my to-do list, and it has been there for more than 10
7years - I remember reading about it in my first year in university,
8so it must have been 2013 or early 2014.
9
10So I started by quickly reading through the first few chapters of
11[the book](https://doc.rust-lang.org/book/) to get an idea of the basic
12syntax. Before I move on to implementing something, I thought I could
13share my very early impression of the language and the tooling around it.
14
15*Note: this is a relaxed write-up and I have a very superficial
16understanding of the topic. I am going to use strong words for the things
17that I did not like, but there are many things I like about Rust so
18far. In fact, my first impression of the language, the documentation and
19the tools is very positive! Keep this in mind while reading this post.*
20
21## What is Rust about?
22
23Before getting started with the Rust book, my impression was that Rust
24was mostly about *safety* as in *memory safety*, and that it achieved
25this by enforcing strict rules, leading to more correct programs overall.
26
27But [the foreword](https://doc.rust-lang.org/book/foreword.html) says "the
28Rust programming language is fundamentally about *empowerment*". Uh, that's
29weird. I was expecting "something something *safety*". And actually,
30I was hoping for "something something *correctness*". Definitely not
31"something something *empowerment*". *Empowerment* sounds like one of
32those meaningless words that managers use when they have nothing to say.
33
34Anyway, empowerement it is. Let's move on to actually using the thing.
35
36## Installation
37
38The officially endorsed way of installing Rust is the following:
39
40```
41curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
42```
43
44The horror, the horror! Is this really what they suggest? The very thing
45that *everyone* told you not to do in nerd forums until a few years ago,
46even worse than copy-pasting commands from the internet, is now THE
47suggested way of installing software? Seriously, piping a random web
48page into `sh`?
49
50I don't know what this script does, is it going to download even more
51random shit from the internet? (Answer: yes, it is!) Is it going to fuck
52up my environment by puking configuration lines into my .bashrc? (Answer:
53yes, it is!)
54
55But ok, what do I know. Maybe I am just leaving in the past, I should
56embrace the future, because package managers and `make && make install`
57*are so 1999, man*. So I took a deep breath an run the command.
58Luckily the script kindly asked "do you want me to fuck up your
59environment? [default: Yes]". I may be paraphrasing this, it probably
60mentioned `.bash_rc` and `.profile`. Anyway, you can easily say "no"
61and skip this fuckery. And then manually update a couple of environment
62variables in your shell configuration file, which is what the script was
63trying to do. I guess developers today are not expected to know how to
64do this.
65
66The installation was quick and everything went fine. Moving on!
67
68## Cargo cult
69
70[Cargo](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html) is
71Rust's build system and all-in-one tool. It can create a new project
72with a default folder structure, compile code (running `rustc` under
73the hood`), run tests, and more. Oh and it is also a *package manager*,
74which apparently is something modern programming languages decided they
75needed - more on that later.
76
77I don't always like these kind of tools, becase they tend to do a lot of
78things that you don't understand with the files in the current directory.
79Like vomiting generated files that you are supposed to check into your
80[VCS](https://en.wikipedia.org/wiki/Version_control) - which kinda
81defeats the purpose of *generating* files in the first place, doesn't it?
82
83Luckily, Cargo does not do too much of this. It generates an `src` folder
84with a simple "hello world" program, a pretty minimal `Cargo.toml`
85configuration file, and it initializes a git repo. All reasonable,
86and most importantly easy to understand.
87
88On the first run of `cargo build` it also generates a `target` folder
89for the build artifacts (already in `.gitignore`) and it generates
90only one of those vomit files that I am supposed to check into git
91(`Cargo.lock`), which I promptly added to .gitignore. Apparently it is
92mainly about dependencies, and I'd rather not use any for now. So all
93in all I am satisfied with this process.
94
95About dependencies, you can specify some packages, or *crates*, that your
96projects depends on. These *crates* are going to be downloaded as they are
97needed from [crates.io](https://crates.io/). You can of course specify
98some constraints on the version for each specific crate, like "at least
992.0" or "at least 0.8.5, but less than 0.9.0". Here is where `Cargo.lock`
100comes into play: if there is more than one version available for a given
101crate that satisfies the contraints you impose in `Cargo.toml`, the first
102time a specific version is used it gets written to `Cargo.lock`, so that
103you will keep using that version even if a new one becomes available.
104You are supposed to check in this file to git so other developers working
105on the same project will use exactly the same version of each dependency.
106
107I can't quite understand this. If I don't want to update to a later
108(minor) version of the dependency, I can already specify this in
109`Cargo.toml`. If instead I impose more relaxed requirements, then it
110means that any version satisfying those requirements is fine, and I
111in this case *I do want* to try different versions within the allowed
112range, to make sure that my assumptions are correct. So I am confused
113about why this Cargo.lock business is needed at all. But I guess if
114the time ever comes that I need to include dependencies in my project -
115probably in the far future, *right?* - then I can just specify an exact
116version and happily ignore `Cargo.lock`.
117
118Anyway, coming from a mostly C and C++ background where there is no
119standard way of including dependencies, all of this is certainly quite
120interesting.
121
122## Dependencies?
123
124Chapter 2 contains a simple code example.
125[This part](https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html#generating-a-secret-number)
126caught my attention:
127
128"Rust doesn't yet include random number functionality in its standard library."
129
130Uh, ok. And then people complain about C having a small standard library.
131It then continues:
132
133"However, the Rust team does provide a
134[`rand` crate](https://crates.io/crates/rand) with said functionality."
135
136Ok, but... if this is provided by the Rust team, why not including it in
137the standard library? Maybe it is still in beta, Rust is not a "finished"
138project as far as I understand. I would think 15 years is enough to ship
139a random number generator, though.
140
141The tutorial tell us to add `rand` to our dependencies in `Cargo.toml`.
142Simple enough. But then, if we launch `cargo build` again:
143
144```
145cargo build
146 Updating crates.io index
147 Locking 15 packages to latest Rust 1.85.0 compatible versions
148 Adding rand v0.8.5 (available: v0.9.0)
149 Compiling proc-macro2 v1.0.93
150 Compiling unicode-ident v1.0.17
151 Compiling libc v0.2.170
152 Compiling cfg-if v1.0.0
153 Compiling byteorder v1.5.0
154 Compiling getrandom v0.2.15
155 Compiling rand_core v0.6.4
156 Compiling quote v1.0.38
157 Compiling syn v2.0.98
158 Compiling zerocopy-derive v0.7.35
159 Compiling zerocopy v0.7.35
160 Compiling ppv-lite86 v0.2.20
161 Compiling rand_chacha v0.3.1
162 Compiling rand v0.8.5
163 Compiling guessing_game v0.1.0 (file:///projects/guessing_game)
164 Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.48s
165```
166
167Woho wait a minute. I have only added one dependency! Just a random
168number generator! Why is it building 15 packages now? Where does all
169of this come from? Surely these are not dependencies dragged in by
170the `rand` crate, *right?*
171
172Well, apparently they are. If we want to generate a random number, we
173need 15 external packages. I would imagine this makes Rust completely
174unusable in a professional setting, because nobody would want to audit
17515 separate crates just to include a random number generator. And no
176professional programmer would include dependencies in a serious project
177without first auditing them, *right?* *RIGHT?!*
178
179I mean, each dependency introduces an extra liability in your project;
180it is code that you can't control and you never know when it is going to
181break. And this is why serious programmers, select dependencies carefully
182and only use them when absolutely necessary... *right?*
183
184Ahah, of course they do not. Remember the
185[letf-pad incident](https://en.wikipedia.org/wiki/Npm_left-pad_incident)?
186Apparently Cargo is of the same breed as npm. And then we wonder why
187the software industry is such a shitshow. *Just keep building on top
188of the house of cards, man. It's going to be fine, man, don't bother
189implementing low-level stuff, that's hard. Just trust random people on
190the internet and ship their code, man.*
191
192Bleah.
193
194## The Rust language
195
196So far I talked mostly about the tooling around Rust, but I said nothing
197about the language itself. I want to write some code before making a
198more informed opinion about it, but the first impression is that it is
199really nice!
200
201I love the type system, especially
202[enums](https://doc.rust-lang.org/book/ch06-00-enums.html)
203and `match`. I like that this and stuff like `Option<T>` are
204first-class citizens; you can do something similar in C++ with
205[`std::variant`](https://doc.rust-lang.org/book/ch06-00-enums.html) and
206[`std::optional`](https://en.cppreference.com/w/cpp/utility/optional.html),
207but the syntax quickly gets messy. I guess this shows the advantage of
208making a new language from scratch instead of being forced to live with
209decades-old syntax for backwards compatibility.
210
211I especially like this mechanism - Rust's enums, or `std::variant`, or
212tagged unions, whatever you like to call them - for error handling. I find
213them a better solution than exception, because they enforce correctness:
214if your function can fail, your coding must handle it; you can't just
215let an error message bubble up from the depths of Hell, wreak havoc in
216your control flow and face the user with an "Object reference not set
217to an instance of an object". I think it is great that Rust endorses
218this more thorough way of error handling from the start.
219
220On the less-nice side of things, apparently
221[integer overflow](https://doc.rust-lang.org/book/ch03-02-data-types.html?highlight=overflow#integer-overflow)
222is still an issue. Eh. At least when compiling in debug mode overflows
223are caught, which is nice. But in release mode the program is
224going to "panic", which I was not expecting. I mistakenly thought
225this was one of the issues that Rust was solving at compile time,
226or maybe even at the level of language specification.
227
228This brings me to another problem that I thought Rust would solve,
229but it does not: accessing a out-of-bound index of an array also leads
230to a panic. I was expecting "don't allow indexes out of bound" to be
231included in Rust's compile-time checks, somehow. For example in Ada you
232can declare an array to accept only values of a specific
233[range type](https://en.wikibooks.org/wiki/Ada_Programming/Types/range),
234so that out-of-bound errors are completely eliminated. Apparently a
235"panic" is memory safe behavior, but it is definitely not *correct*
236behavior. I guess this was a big misunderstanding from my side: memory
237*safety* does not mean memory *correctness* - Rust still allows you to
238make mistakes related to memory.
239
240## Documentation
241
242A quick note on the documentation: it is very nice. The book is well
243written, and I am enjoying reading through it, even though it is very
244basic for me. It is also available offline - at least when installing
245rust via `rustup` - which is something I always appreciate.
246
247The compiler error messages, which I consider to be part of the
248documentation as well, are simply amazing. Not only they are usually very
249precise, not only they often suggest a fix, but they also point you out to
250some piece of documentation so that you can learn why your code is wrong.
251For example, if you try to compile this code with `rustc filename.rs`:
252
253```
254fn main() {
255 println("Hello, world!");
256}
257```
258
259You get:
260
261```
262error[E0423]: expected function, found macro `println`
263 --> hello.rs:2:2
264 |
2652 | println("Hello, world!");
266 | ^^^^^^^ not a function
267 |
268help: use `!` to invoke the macro
269 |
2702 | println!("Hello, world!");
271 | +
272
273error: aborting due to 1 previous error
274
275For more information about this error, try `rustc --explain E0423`.
276```
277
278And if you run `rustc --explain E0423` as suggested:
279
280```
281An identifier was used like a function name or a value was expected and the identifier exists but it belongs to a different namespace.
282
283Erroneous code example:
284
285struct Foo { a: bool };
286
287let f = Foo();
288// error: expected function, tuple struct or tuple variant, found `Foo`
289// `Foo` is a struct name, but this expression uses it like a function name
290
291Please verify you didn't misspell the name of what you actually wanted to use here. Example:
292
293fn Foo() -> u32 { 0 }
294
295let f = Foo(); // ok!
296
297It is common to forget the trailing ! on macro invocations, which would also yield this error:
298
299println("");
300// error: expected function, tuple struct or tuple variant,
301// found macro `println`
302// did you mean `println!(...)`? (notice the trailing `!`)
303
304Another case where this error is emitted is when a value is expected, but something else is found:
305
306pub mod a {
307 pub const I: i32 = 1;
308}
309
310fn h1() -> i32 {
311 a.I
312 //~^ ERROR expected value, found module `a`
313 // did you mean `a::I`?
314}
315```
316
317I would not be surprised if the main reason why Rustaceans are so
318enthusiastic about the language was actually how nice `rustc` is.
319I expect working with this tool will be very pleasant.
320
321## Moving on!
322
323Apart from the
324[culture shock](https://en.wikipedia.org/wiki/Culture_shock) of the
325installation process and the dependency management, my first impression
326of Rust is quite positive. But as I said, I am just getting started
327and my judgement is very superficial. I do want to write some small
328project in Rust, and I think I'll start from re-writing a simple [math
329library for modular arithmetic](../2025-01-21-taming-cpp-templates)
330that I wrote in C++ some time ago.
331
332Stay tuned for more 🦀

Generated with cgit - Back to sebastiano.tronto.net