
Rust Memory Management - jeena
http://zee-nix.blogspot.com/2017/05/rust-memory-management.html
======
pjungwir
As someone whose career has been mostly in Java/Python/Ruby but is also
comfortable with C, learning Rust has been like a step function, where each
time I get stuck I correct some discrete misperception and then have an easier
time. I wish I had written these down, but here is one that helped me a lot
and is also a little funny:

When Rust folks talk about "move", it is not just jargon about ownership. They
actually mean move to a new place in RAM. If you could do a printf("%p", foo)
before and after, it would be different. So obviously if there are other
pointers to that object, they aren't going to be valid anymore. Figuring this
out really helped me reason about what was allowed and what wasn't. Maybe it
will help out someone else too. "Oh, you mean _move_!" :-)

~~~
khuey
> When Rust folks talk about "move", it is not just jargon about ownership.
> They actually mean move to a new place in RAM. If you could do a
> printf("%p", foo) before and after, it would be different.

That's not really true though. In an optimized build rustc will elide memcpys
whenever possible, which it often is. "Move" in Rust really is about
ownership.

~~~
pjungwir
That is true, but personally I think about that as coming "after" all the
ownership stuff checks out, and as an implementation detail that I can usually
ignore. I do keep it in the back of my mind though.

Btw I have a saying: when you teach, at first you have to lie a little. You
paint the broad strokes, then you refine, then refine again, etc. So it's a
question of what is a helpful mental model. Knowing the optimization behavior
is helpful for some things, but for figuring out valid memory behavior it's
useful to pretend you don't know about that.

------
sevensor
This is the clearest, most concise explanation of these issues that I've seen.
Certainly not comprehensive, but coming from a C perspective it answers about
95% of my questions. This may be the post that gets me actually writing
something in Rust.

~~~
leshow
I'm surprised. I read through it and thought it wasn't very good. I've been
writing Rust here and there for some time now. It seemed like there were a lot
of errors in the post.

~~~
zeenix147
I'm the author of that post. I'm sorry to hear that my post wasn't useful and
contained errors. I'd be very much interested in hearing what the errors are,
so I can correct them. I actually tried all the examples so I'm guessing the
samples at least are correct.

------
jayflux
Very good, enjoyed that, was very clear and easy to follow. For anyone who's
hungry for more I recommend the Rust Book Second Edition (not finished yet)
[https://rust-lang.github.io/book/second-edition/index.html](https://rust-
lang.github.io/book/second-edition/index.html)

I hope he does one for lifetimes as I still find them confusing

~~~
zeenix147
Thanks. Haha, me too. :) I'll try to write one when I feel comfortable enough
with lifetimes myself.

------
falcolas
> stack allocation is preferred over the heap allocation and that's where
> everything is allocated by default

Has this ever been proven to be a problem in practice, given that a program's
stack is typically limited in size? Would the recommended workaround (if this
was a problem) be to change variables to use heap instead?

~~~
dom0
Stack allocation is the default in all practically used languages that
actually have a stack (e.g. C, C++), and heap allocation is explicit in all of
those as well.

Allocation more or less everything on the heap (i.e. using the stack only for
calls and returns) is something that's done out of necessity in exclusively
GC'd languages, like Java or Python.

~~~
falcolas
Sure, it's the default, but C and C++ encourage have patterns where calls to
`malloc` and `new` much more common than in Rust. Especially since strings are
almost always handled as malloc'ed buffers.

I'm just wondering if the extra emphasis on stack allocation has created
issues; this article doesn't cover the allocation of Strings or Vecs (wrapping
them in RC and ARC boxes), and most other references on the net are a few
years old now.

~~~
masklinn
> I'm just wondering if the extra emphasis on stack allocation has created
> issues; this article doesn't cover the allocation of Strings or Vecs

As in C++, these are small stack structures (a few words, IIRC it's 3 for Vec,
and String is backed by a Vec) pointing to a heap-allocated buffer.

------
partycoder
It's great to see Gnome adopting Rust. Vala was OK, but we all Vala compiled
to C rather than binary.

I think the move to Rust should attract more developers to the Gnome
ecosystem. The learning curve of C or Vala + GTK + GObject and related tech
can be a bit steep.

------
Symmetry
I'm not sure I understand why, in design terms, add_first_element doesn't
return ownership of the vectors given that they don't seem to be passed in
mutably?

~~~
pornel
It's by design - this syntax intentionally expresses such contract.

As a matter of policy Rust doesn't try to figure out behavior of functions
from their bodies, because that would mean changes in the implementation could
accidentally break the API.

While in this example taking ownership is pointless, there are cases where
it's meanigful: e.g. a method that closes a file handle would take ownership
of the handle to ensure that the handle can't be used anywhere else after it's
closed.

In Rust ownership allows mutability (if you own an object, and there are no
other references to it, you can do whatever you want to it).

~~~
Symmetry
Wouldn't closing a file handle be an action requiring mutability? If the file
handle is just a integer then it would be copyable and ownership wouldn't come
up as an issue meaning discussions of safety would be moot.

I understand not trying to look at function bodies but the mutability of a
function seems to be strictly a property of it's type signature.

But I think what really bothers me is that whether a value is actually passed
to a function by pointer or by copying it onto the stack is governed by
whether it has the 'copy' trait - not something obvious to the casual reader
of the program. Silently converting what looks like a pass by value call to an
in-practice pass by reference based on a trait definition elsewhere in the
code base just seems excessively magical to me and I'd be much happier if the
compiler threw an error at

let answer = add_first_element(v1, v2);

because pass by value requires the copy trait which Vecs v1 and v2 don't have.

~~~
masklinn
> Wouldn't closing a file handle be an action requiring mutability?

Closing a file requires _ownership_ and is actually an RAII action: when the
file object itself goes out of scope (which by definition means all extant
references have been removed) the handle is closed.

> But I think what really bothers me is that whether a value is actually
> passed to a function by pointer or by copying it onto the stack is governed
> by whether it has the 'copy' trait - not something obvious to the casual
> reader of the program.

That's not correct. The Copy trait means it's semantically copied rather than
moved so the original value can still be used, it does not change the
technicalities of parameter passing which is always by-value (unless you're
explicitly creating a reference) although things may then get optimised
differently.

> Silently converting what looks like a pass by value call to an in-practice
> pass by reference

There's no such thing. Both Vecs are passed by value. Outside of method calls
(which have some syntactic sugar) Rust requires references to be created
explicitly.

> I'd be much happier if the compiler threw an error at

> let answer = add_first_element(v1, v2);

> because pass by value requires the copy trait which Vecs v1 and v2 don't
> have.

It has no reason to and it does not. `add_first_element` gets its argument by
value, since Vec is not a Copy type v1 and v2 are thus _moved_ into the
function and not accessible anymore from outside it.

~~~
Symmetry
>Closing a file requires ownership and is actually an RAII action: when the
file object itself goes out of scope (which by definition means all extant
references have been removed) the handle is closed.

Ah, no wonder I misunderstood then. I'd forgotten that RAII was something Rust
had and that feature does very nicely explain that design decision.

I suppose the reason a Vec doesn't implement Copy then is that its members
might not, like a file handle?

~~~
masklinn
> I suppose the reason a Vec doesn't implement Copy then is that its members
> might not, like a file handle?

That is correct, Vec has a pointer to a heap allocated buffer, meaning if Vec
were Copy, you'd get two Vec structures for the same backing buffer and that's
a recipe for disaster.

