Rust Tidbits: (Almost) all the ways to declare (im)mutability using let, mut and &
mut, &mut, let mut, let &mut …… Oh my!
This post covers the little journey I took to learn the various ways that the mut
, &
and let
keywords can be used together. For the impatient, here’s the summary (but read on if you are interested in the details and examples):
We are going to cover the following (examples to follow below):
// Assume v does not implement the Copy trait. For example, String::from("chicken").// move v into b
// you can not modify b
let b = v// attempt to destructure to move v to b
let &b = &v
let &mut b = v
let &mut b = &mut v// invalid syntax
let b = mut v// b is immutably bonded to a mutable reference of v
// i.e. you can modify v through b
// you can not bind b to another &mut v2, in which v and v2 share the same type T.
let b = &mut v// v is moved into b and made mutable
// i.e. you can modify v through b
// you can rebind b to another v2, in which v and v2 share the same type T
// v is moved so it can no longer be borrowed again
let mut b = v// b is mutably bonded to an immutable reference to v
// i.e. you can not modify v through b
// you can bind b to another &v2, in which v and v2 share the same type T
// v is not moved so can be borrowed again later once b is rebind to another reference
let mut b = &v// b is mutably bonded to a mutable reference of v
// i.e. you can modify v through b
// you can bind b to another &mut v2, in which v and v2 share the same type T
// v is not moved so can be referenced again later once b is rebind to another mutable reference
let mut b = &mut v
let b = v
This is the most straight-forward one. We simply move v
into b
.
let &b = &v
This snippet will not compile. It uses the destructuring syntax and try to move the value v
to b
. In other words, the snippet above means essentially the following:
let b = mut v
Line 2 contains invalid syntax and will not compile. If you think about it carefully, what you actually meant to write is probably: “give me a reference to v
called b
so that I can mutate v
through b
” instead of “give me a mutable v
called b
”, which leads us to the next snippet.
let b = &mut v
b
is bind to a mutable reference of v
and this means that we can modify v
by changing b
. Be ware that in order to bind a mutable reference of v
to another variable, v
itself has to be mutable in the first place (line 2).
In addition, since b
is immutably bind to a mutable reference of v
, we can no longer bind b
to another mutable reference. So the following snippet will not compile due to line 7:
let mut b = v
v
is moved into b
and made mutable. Note that the type of b
is String
not &String
.
Since b
is mutable, we can reassign a different value to it.
However, since v
was moved, we can no longer borrow it afterward (different from let mut b = &mut v
), so the following will not compile:
let mut b= &v
The snippet above can be successfully compiled since b
is “rebindable”, meaning it’s free to bind to whatever &String
we would like to. In the code snippet, you can see that we first bind b to &v
then to &v2
.
let &mut b= v
This snippet will not compile. It uses the destructuring syntax and try to move the value v
to b
. In other words, the snippet above means essentially the following:
let mut b = &mut v
This snippet can be compiled. We bind a mutable reference of v
to b
, which implies we can modify v
through b
. And since we use the mut
keyword when declaring b
, we can “rebind” b
to other references if we want to. Note that v
can be borrowed again after we rebind &mut v2
to b
on line 8. Like this on line 10:
let &mut b = &mut v
This snippet will not compile. It again uses the destructuring syntax and try to move the value v
to b
. In other words, the snippet above means essentially the following:
Conclusion
Please let me know if you find any error (which is likely in a post like this)
:)