Could someone please explain to me the difference between these four function definitions:
fn update(&state: KeyboardState) {...}
fn update(&mut state: KeyboardState) {...}
fn update(state: &KeyboardState) {...}
fn update(state: &mut KeyboardState) {...}
I'm pretty sure I get the & vs &mut, but I'm still unclear on the difference between putting it before and after the parameter name.
On a related note, here's another question I'm not quite clear on: What's the difference between a pointer and a reference? Thank you!
The first two are not legal syntax, actually. I think what you meant was:
fn update(state: KeyboardState) { ... }
fn update(mut state: KeyboardState) { ... }
It's a little clearer with variable bindings, rather than arguments, even though they're the same:
let state: &KeyboardState;
let mut state: &KeyboardState;
let state: &mut KeyboardState;
let mut state: &mut KeyboardState;
These are the four options. In the second and fourth, the binding is mutable, and in the third and fourth, the reference is mutable. You can reassign a mutable binding, but you can modify what a mutable binding points to.
Mutability elaborates a bit; did you read it?
We tend to say "reference" always, because while references and C's "pointers" are the same thing at an assembly level, C doesn't check the validity of pointers, but Rust does check the validity of references. So in some sense, "reference" is a specific kind of pointer. Does that make sense?
I did read the mutability chapter, but that was several weeks ago. Must have forgotten some parts.
Regarding the syntax, if
fn update(&state: KeyboardState) {..}
isn't valid syntax, why doesn't the compiler give me some form of syntax error? It just says:
error: mismatched types:
expected `KeyboardState`,
found `&_`
(expected struct `KeyboardState`,
found &-ptr) [E0308]
And yes, the references explanation makes sense. Thank you!
It's perfectly valid syntax (steveklabnik has forgotten that the first part of a parameter is a pattern, not an identifier).
Because it's a pattern, putting it there does roughly the opposite of what putting it in an expression does. If I write this:
let &v = &3i32;
v
will be of type i32. Because the pattern &v
was bound to &3
, v
was bound to 3
. It destructured it. Function parameters work just like let bindings do.
With this information, it makes sense that you got a type error. You tried to bind KeyboardState
to &k
, but there's no &
on the one side to bind the &
to on the other side.
This would be valid (though it will give an error in the borrow checker unless KeyboardState
is copyable, because getting the KeyboardState
out of a &KeyboardState
requires copying it).
fn update(&state: &KeyboardState) {...}
Also, we always say reference in regards to &T
. *const T
is a raw pointer, because the compiler does not track it at all.
Thank you all for the help! Rust is different, but I think I'm getting there
I think the mutability docs have one confusing part:
let mut x = 5;
This is a mutable variable binding. When a binding is mutable, it means you’re allowed to change what the binding points to.
And then it says:
If you want to change what the binding points to, you’ll need a mutable reference:
Which is exactly the same phrase ("to change what the binding points to").
I understand that the context is different, but this is so central and sometimes subtle (as the docs also say) that maybe another phrasing could be better.
The examples help a lot, though.
Would this be a better phrasing?
You can also create a reference to it, using &x
, but if you want to use the reference to change it, you will need a mutable reference.
let mut x = 5;
let y = &mut x;
y is an immutable binding to a mutable reference, which means that you can’t bind y to something else (y = &mut z
), but y
can be used to bind x
to something else (*y = 5
). A subtle distinction.
p.s. I will open a pull request, if you say yes.
1 Like
@notriddle It does help a lot! Thanks for taking the time.