Understanding Memory Management, Part 5: Fighting with Rust

(educatedguesswork.org)

73 points | by Curiositry 2 days ago ago

11 comments

  • steveklabnik 3 hours ago ago

    This is a good post! A few comments:

    > Function Overloads

    Strictly speaking, Rust doesn't support overloaded functions. Function overloading is when you define the same function but with different arguments, and the language selects the correct one based on the argument type. In this case, it's two different implementations of a trait for two different types. That said, it's close enough that this isn't really an issue, more of just a technical note, since this is a series trying to get into details.

    > I can't find an explanation in the Rust documentation but I expect the reason is that someone could implement another trait that provides .into_iter() on whatever the x is in for y in x, thus resulting in a compiler error because there would be two candidate implementations.

    Yep, I'm not sure that there is an official explanation anywhere else, but this is exactly what I would assume as well. This ensures that the correct implementation is called. This is also one of the reasons why adding a trait implementation isn't considered a breaking change, even if it could create a compiler error, because you can always expand it yourself to explicitly select the correct choice. Of course, these situations are usually treated more carefully then they have to be, because breakage isn't fun, even if it's technically allowed.

    > But wait, you say, I'm doing exactly this in the first program, and indeed you are.

    It's not the same, as the next paragraphs explain.

    > We are able to examine the function and realize it's safe, but because the compiler wants to use local reasoning, it's not able to do so.

    This is a super important point!

    • junon an hour ago ago

      > I can't find an explanation in the Rust documentation but I expect the reason is that someone could implement another trait that provides .into_iter() on whatever the x is in for y in x, thus resulting in a compiler error because there would be two candidate implementations.

      Because nothing in Rust is identifier-based. Unlike python, all syntax magic (even the ? operator) relies on traits defined by `core`.

          for x in y {
      
      desugars to

          let mut iter = IntoIterator::into_iter(y);
          while let Some(x) = y.next() {
      
      and

          x?
      
      desugars to

          match x {
              Ok(x) => x,
              Err(e) => {
                  return Err(From::from(e));
              }
          }
      
      and

          x + y
      
      desugars to

          core::ops::Add::add(x, y)
      
      etc.

      All of those traits are expected to live explicitly in the core crate at well known paths. Otherwise you'd be writing methods with absolutely no idea how the language would interact with it. And if you had a Set type implement `add`, it'd have to accept exactly 2 arguments to be compatible with the language's `add` or something equally as unergonomic.

      It's traits all the way down! There'd be no explanation needed because it'd be antithetical and contradictory to traits to begin with. Once one understands how traits are intended to be used, the explanation for why there aren't identifier based resolution semantics becomes obvious.

  • bsaul 7 minutes ago ago

    "If we change .set_value() to take a &self instead of a &self"

    guess it's "instead of a &mut self"

  • Animats 3 hours ago ago

    This is pretty good.

    A useful way to think about this:

    - All data in Rust has exactly one owner.

    - If you need some kind of multiple ownership, you have to make the owner be a reference-counted cell, such as Rc or Arc.

    - All data can be accessed by one reader/writer, or N readers, but not both at the same time.

    - There is both compile time and run time machinery to strictly enforce this.

    Once you get that, you can see what the borrow checker is trying to do for you.

  • Surac 3 hours ago ago

    I really like the text. Giving more light to the memory management of rust will help me understand more of the language. I still think some concepts of rust are over verbose but I slowly understand the hype around rust. I myself use C or C++ but I will „borrow“ some of the rust ideas to make my code even more robust

    • ultimaweapon an hour ago ago

      I'm coming from C++ now I don't want to use C++ anymore. When C++ was still my primary language I always frustrated with some of its feature like non-destructive move, copy by default and dangling references then I found Rust fixed all of those problems. At the beginning I very frustrated with Rust because the borrow checker prevent me from doing what I usually do in C++ but I keep going.

  • sidcool 4 hours ago ago

    This is brilliantly written n

    • bnjms 4 hours ago ago

      Im sure it is. I don’t know how to program but read most of part 4 when it appeared here last.

  • cornholio 2 hours ago ago

    A 20 page document on how to use basic variables, function calls and methods. Except for the threading paragraph, which is hard in any language, this is all complexity and refactoring pain that Rust hoists onto every programmer every day, for relatively modest benefits, somewhat improved performance and memory usage vs the garbage collected/ref-counted version of the same code.

    Essentially, you wouldn't and shouldn't make that tradeoff for anything other than system programming.

    • bsaul a minute ago ago

      I see why you're saying that, and i almost entirely agree with you. However, i would say that if all you're doing is glueing calls to third party systems (like what most backend code is), then you won't fall into complex lifetime problems anyway, and the experience will remain quite pleasant.

      Another point, is that the rust ecosystem is absolutely insanely good ( i've recently worked with uniffi and wasmbindgen, and those are 5 years ahead of anything else i've seen..)

    • baq 2 hours ago ago

      > this is all complexity and refactoring pain that Rust hoists onto every programmer every day

      This is what you should be doing when working with C/C++, except there is no compiler to call you names there if you don’t.

      If you’re saying ‘use a GC language unless requirements are strict about it’, yeah hard to disagree.