What's the semantic of assignment in Rust? - rust

How could know the type of a binding if I use auto type deduction when creating a binding? what if the expression on the right side is a borrow(like let x = &5;), will it be value or a borrow? What will happen if I re-assign a borrow or a value?
Just for check, I do can re-assign a borrow if I use let mut x: &mut T = &mut T{}; or let mut x:&T = & T{};, right?

I sense some confusion between binding and assigning:
Binding introduces a new variable, and associates it to a value,
Assigning overwrites a value with another.
This can be illustrated in two simple lines:
let mut x = 5; // Binding
x = 10; // Assigning
A binding may appear in multiple places in Rust:
let statements,
if let/while let conditions,
cases in a match expression,
and even in a for expression, on the left side of in.
Whenever there is a binding, Rust's grammar also allows pattern matching:
in the case of let statements and for expressions, the patterns must be irrefutable,
in the case of if let, while let and match cases, the patterns may fail to match.
Pattern matching means that the type of the variable introduced by the binding differs based on how the binding is made:
let x = &5; // x: &i32
let &y = &5; // y: i32
Assigning always requires using =, the assignment operator.
When assigning, the former value is overwritten, and drop is called on it if it implements Drop.
let mut x = 5;
x = 6;
// Now x == 6, drop was not called because it's a i32.
let mut s = String::from("Hello, World!");
s = String::from("Hello, 神秘德里克!");
// Now s == "Hello, 神秘德里克!", drop was called because it's a String.
The value that is overwritten may be as simple as an integer or float, a more involved struct or enum, or a reference.
let mut r = &5;
r = &6;
// Now r points to 6, drop was not called as it's a reference.
Overwriting a reference does not overwrite the value pointed to by the reference, but the reference itself. The original value still lives on, and will be dropped when it's ready.
To overwrite the pointed to value, one needs to use *, the dereference operator:
let mut x = 5;
let r = &mut x;
*r = 6;
// r still points to x, and now x = 6.
If the type of the dereferenced value requires it, drop will be called:
let mut s = String::from("Hello, World!");
let r = &mut s;
*r = String::from("Hello, 神秘德里克!");
// r still points to s, and now s = "Hello, 神秘德里克!".
I invite you to use to playground to and toy around, you can start from here:
fn main() {
let mut s = String::from("Hello, World!");
{
let r = &mut s;
*r = String::from("Hello, 神秘德里克!");
}
println!("{}", s);
}
Hopefully, things should be a little clearer now, so let's check your samples.
let x = &5;
x is a reference to i32 (&i32). What happens is that the compiler will introduce a temporary in which 5 is stored, and then borrow this temporary.
let mut x: &mut T = T{};
Is impossible. The type of T{} is T not &mut T, so this fails to compile. You could change it to let mut x: &mut T = &mut T{};.
And your last example is similar.

Related

cannot assign to `value`, which is behind a `&` reference [duplicate]

Here are two function signatures I saw in the Rust documentation:
fn modify_foo(mut foo: Box<i32>) { *foo += 1; *foo }
fn modify_foo(foo: &mut i32) { *foo += 1; *foo }
Why the different placement of mut?
It seems that the first function could also be declared as
fn modify_foo(foo: mut Box<i32>) { /* ... */ }
If you're coming from C/C++, it might also be helpful to think of it basically like this:
// Rust C/C++
a: &T == const T* const a; // can't mutate either
mut a: &T == const T* a; // can't mutate what is pointed to
a: &mut T == T* const a; // can't mutate pointer
mut a: &mut T == T* a; // can mutate both
You'll notice that these are inverses of each other. C/C++ take a "blacklist" approach, where if you want something to be immutable you have to say so explicitly, while Rust takes a "whitelist" approach, where if you want something to be mutable you have to say so explicitly.
mut foo: T means you have a variable called foo that is a T. You are allowed to change what the variable refers to:
let mut val1 = 2;
val1 = 3; // OK
let val2 = 2;
val2 = 3; // error: re-assignment of immutable variable
This also lets you modify fields of a struct that you own:
struct Monster { health: u8 }
let mut orc = Monster { health: 93 };
orc.health -= 54;
let goblin = Monster { health: 28 };
goblin.health += 10; // error: cannot assign to immutable field
foo: &mut T means you have a variable that refers to (&) a value and you are allowed to change (mut) the referred value (including fields, if it is a struct):
let val1 = &mut 2;
*val1 = 3; // OK
let val2 = &2;
*val2 = 3; // error: cannot assign to immutable borrowed content
Note that &mut only makes sense with a reference - foo: mut T is not valid syntax. You can also combine the two qualifiers (let mut a: &mut T), when it makes sense.
The following natural language translation seems to clear things up for me...
let x = value;
x {binds immutably} to {immutable value}
let mut x = value;
x {binds mutably} to {possibly mutable value}
let x = &value;
x {binds immutably} to {a reference to} {immutable value}
let x = &mut value;
x {binds immutably} to {a reference to} {mutable value}
let mut x = &value;
x {binds mutably} to {a reference to} {immutable value}
let mut x = &mut value;
x {binds mutably} to {a reference to} {mutable value}
where
{binds mutably} means the binding can be reassigned
{mutable value} means the value's contents can change
To be able to mutate a value you need both a mutable binding and a mutable value
Note:
Reference mutability vs target mutability
A reference variable such as x, as in let x = &mut y, is a separate variable from the target variable y it is pointing to. In particular, x has its own location on the stack and mutability permissions. As such, if x is immutable, as it is here, then it cannot be reassigned to point to some other variable. That restriction is separate from the ability to mutate the target through it, as in *x = some_value; the target is a distinct variable with its own mutability permissions. However, if w is mutable, as in let mut w = &mut p, then it can indeed be reassigned to point to some other similarly typed variable: w = &mut z.
Mutable Variable of Reference Type
When you have
let mut x: &T = value;
This means that x is variable that refers to an instance of T, as if by storing the memory address of the T instance in x's value. This reference (i.e. the "memory address") is the subject of mutability in this context: x can be modified to refer to a different instance of T like this:
x = some_other_T_instance;
but the referant (i.e. the value of the T instance to which x refers) cannot be changed via x:
// Illegal
*x = a_different_value;
Immutable variable of Mutable Reference type
When you have
let x: &mut T = value;
This means that x is a variable that refers to a mutable instance of T. In this case, the referent (i.e. the actual value) is the subject of mutability. It can be modified through the reference like this
*x = some_other_value;
but the reference itself (i.e. the "memory address" in the variable x) cannot:
// illegal
x = a_different_value;

Why is the compiler letting me borrow another borrow in a box?

I'm wondering if someone can help me understand why this program behaves as it does:
fn main() {
let mut x = 456;
{
let mut y = Box::new(&x);
y = Box::new(&mut y);
println!("GOT {}",*y);
}
}
This program compiles under rust 1.35.0 (both 2015 and 2018 editions), and prints
GOT 456
But, I'm confused what's going on here. I'm guessing that this is an example of an auto-dereference. So, in reality, it looks like this:
fn main() {
let mut x = 456;
{
let mut y = Box::new(&x);
y = Box::new(&mut *y);
println!("GOT {}",*y);
}
}
Is that it?
This is a case of deref coercion, but one that is obfuscated by a few other unnecessary parts of the code. The following improvements should be made here:
The mut modifier on variable x is not needed because it is never modified.
The borrow of y in Box::new(&mut y) does not have to be mutable because the variable holds an immutable reference.
The println! implementation also knows to print values behind references, so the explicit * is not needed.
Then, we get the following code:
fn main() {
let x = 456;
{
let mut y = Box::new(&x);
y = Box::new(&y);
println!("GOT {}", y);
}
}
y is a variable of type Box<&i32> which is initially bound to a box created in the outer scope. The subsequent assignment to a new box works because the &y, of type &Box<&i32>, is coerced to &&i32, which can then be put in the box by automatically dereferencing the first borrow. This coercion is required because the variable x can only be assigned values of the same Box<&i32> type.
The lifetime of the reference inside both boxes also ended up being the same, because they refer to the same value in x.
See also:
What is the relation between auto-dereferencing and deref coercion?

Confused about ref keyword usage in closures

I'm trying to understand how & and ref correspond. Here's an example where I thought these were equivalent, but one works and the other doesn't:
fn main() {
let t = "
aoeu
aoeu
aoeu
a";
let ls = t.lines();
dbg!(ls.clone().map(|l| &l[..]).collect::<Vec<&str>>().join("\n")); // works
dbg!(ls.clone().map(|ref l| l[..]).collect::<Vec<&str>>().join("\n")); // doesn't work
dbg!(ls.clone().map(|ref l| &l[..]).collect::<Vec<&str>>().join("\n")); // works again!
}
From the docs:
// A `ref` borrow on the left side of an assignment is equivalent to
// an `&` borrow on the right side.
let ref ref_c1 = c;
let ref_c2 = &c;
println!("ref_c1 equals ref_c2: {}", *ref_c1 == *ref_c2);
What would the equivalent to |l| &l[..] be with |ref l|? How does it correspond to the assignment examples in the docs?
Taking a look at the docs page for Lines(The iterator adapter for producing lines from a str), we can see that the item produced by it is:
type Item = &'a str;
Therefore the following happens when trying to do the "doesn't work" version:
dbg!(ls.clone().map(|ref l| l[..]).collect::<Vec<&str>>().join("\n")); # doesn't work
//Can become:
let temp = ls
.clone()
.map(|ref l| l[..])
.collect::<Vec<&str>>()
.join("\n");
println!("{}", temp);
Here we can see a crucial problem. l if of type &&str (Which I will explain below) so indexing into it will create a str, which is unsized and therefore cannot be outside of a pointer of some sort.
Now, onto the real thing you wanted to learn: What a ref pattern does:
When doing pattern matching or destructuring via the let binding, the ref keyword can be used to take references to the fields of a struct/tuple.
What this does is the following:
When we have let ref x = y, we take a reference to y.
When pattern matching on something (Like in your closure arguments you showed) we have a slightly different effect: the value under the reference is moved into the scope and then taken reference to while exposing a way to take the value under the reference. For example:
fn foo(ref x: String) {}
let y: fn(String) = foo;
This works because what is essentially being done is this:
fn foo(x: String) {
let x: &String = &x;
}
So what ref x does is take ownership of x and produce a reference to it.
On the other hand
When we have let &x = y we move a value out of y.
This is the opposite of ref, in that we take ownership of the value under y if we can. For example:
let x = 2;
let y = &x;
let &z = y; //Ok, we're moving a `Copy` type
This is only ok for copy types though as though this isn't exactly the same as let x = *y which would work for owned Boxes.

Why does this Rust program ignore immutability

I have the following Rust program and I expect it to result in an compilation error since x is reassigned later. But it complies and gives output. Why?
fn main() {
let (x, y) = (1, 3);
println!("X is {} and Y is {}", x, y);
let x: i32 = 565;
println!("Now X is {}", x);
}
Rust actually lets you shadow other variables in a block, so let x: i32 = 565; is defining a new variable x that shadows the x defined earlier with let (x,y) = (1,3);. Note that you could even have redefined x to have a different type since the second x is a whole new variable!
fn main(){
let x = 1;
println!("Now X is {}",x);
let x = "hi";
println!("Now X is {}",x);
}
This reddit thread goes into more detail about why this is useful. The two things that are mentioned that seem interesting are:
For operations which take ownership of the variable, but return another variable of the same type, it sometimes "looks nice" to redefine the returned variable to have the same name. From here:
let iter = vec.into_iter();
let iter = modify(iter);
let iter = double(iter);
Or to make a variable immutable:
let mut x;
// Code where `x` is mutable
let x = x;
// Code where `x` is immutable

How can I introduce a copied variable as mutable in a if-let statement?

I have a HashMap<i8, i8> which could contain cycles:
let mut x: HashMap<i8, i8> = HashMap::new();
x.insert(1, 6);
x.insert(3, 5);
x.insert(5, 1);
To get the final value for 3, it should first lookup x[3], then x[5] and finally x[1] which should yield 6. I decided to use a while let loop:
let mut y = x[&3]; // y: i8
while let Some(&z) = x.get(&y) {
y = z;
}
println!("{}", y);
x.insert(0, 0);
This works fine, but it would panic! if 3 is not in the map. As I don't want to do anything about the None case, I want to use a if let (similar to the while let used).
I have tried some notations:
if let Some(&y) = x.get(&3): copies the value, but y is immutable (y: i8)
if let Some(mut y) = x.get(&3): y is mutable, but the value is borrowed (mut y: &i8)
if let mut Some(&y) = x.get(&3): my target: mutable copy, but invalid syntax (mut y: i8)
(All variants are available at Rust Playground, but you need to comment out the third try, as it is invalid syntax)
I would not argue about the second variant, but I need to insert values into my map in the body of the if let. As the map remains borrowed, I can't insert anymore. All I would need is that the value in Some(y) is copied, and y is mutable, so that the borrow checker is satisfied and I can do my recursive lookups.
Your approach #1 is a perfectly correct match, you just need to make the y variable mutable. One possibility is to convert Option<&i8> to Option<i8>, enabling the use of mut y in the pattern. For example, Option::map can dereference the value:
if let Some(mut y) = x.get(&3).map(|ref| *ref) {
Since Copy implies (cheap) Clone, you can express the same using Option::cloned():
if let Some(mut y) = x.get(&3).cloned() {
As of Rust 1.35, you can use Option::copied(), which is only defined for Copy types and just copies the value:
if let Some(mut y) = x.get(&3).copied() {
Another possibility is to leave your approach #1 as-is, but correct it simply by introducing a separate mutable variable inside the if let block:
if let Some(&y) = x.get(&3) {
let mut y = y;
...
Your code basically works:
use std::collections::HashMap;
fn main() {
let mut x: HashMap<i8, i8> = HashMap::new();
x.insert(1, 6);
x.insert(3, 5);
x.insert(5, 1);
let mut key = 3;
while let Some(&z) = x.get(&key) {
key = z;
}
println!("{}", key);
x.insert(key, 0);
}
Here, key is left as the last key that did not match.

Resources