sebastiano.tronto.net

Source files and build scripts for my personal website
git clone https://git.tronto.net/sebastiano.tronto.net
Download | Log | Files | Refs | README

cargo-culture-shock.md (14437B)


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