rustlings
rustlings copied to clipboard
move_semantics5 makes use of "*", which has not been explained yet
This exercise could use a refactor where we don't use the *
operator. The *
is confusing/distracting at this stage for beginners. Moreover, the readme for the move_semantics section doesn't link to any page explaining what *
does.
Couldn't we use String or Vec like in the linked docs instead of playing with dereferencing references to a variable on the stack?
+1 I see two mutable refs to x
in this exercise.
But it impossible by rust design.
Why it works?
fn main() {
let mut x = 100;
let y = &mut x;
let z = &mut *y;
// y and z is mutable references to x at the same time, it isn't?
*z += 1000;
*y += 100;
assert_eq!(x, 1200);
}
@bm0 It does not work though (on purpose):
error[E0503]: cannot use `*y` because it was mutably borrowed
--> src/main.rs:8:5
|
5 | let z = &mut *y;
| ------- borrow of `*y` occurs here
...
8 | *y += 100;
| ^^^^^^^^^ use of borrowed `*y`
9 | *z += 1000;
| ---------- borrow later used here
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7a19b93609ece84708875c8eb375e722
The point of the exercise is making it work by reordering:
fn main() {
let mut x = 100;
let y = &mut x;
*y += 100;
let z = &mut *y;
*z += 1000;
assert_eq!(x, 1200);
}
Playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=bd6d10a01a666cd7bc6c63ff961e64f4
This is possible because the y
and z
references are no longer used after the mutations, and are dropped.
The borrow checker will refuse compilation if you do use y
and z
later again,
e.g. if you add println!("{} {} {}", x, y, z);
to the end of the function:
error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
--> src/main.rs:9:26
|
4 | let y = &mut x;
| ------ mutable borrow occurs here
...
9 | println!("{} {} {}", x, y, z);
| ^ - mutable borrow later used here
| |
| immutable borrow occurs here
error[E0502]: cannot borrow `y` as immutable because it is also borrowed as mutable
--> src/main.rs:9:29
|
6 | let z = &mut *y;
| ------- mutable borrow occurs here
...
9 | println!("{} {} {}", x, y, z);
| ^ - mutable borrow later used here
| |
| immutable borrow occurs here
error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
--> src/main.rs:8:5
|
4 | let y = &mut x;
| ------ mutable borrow occurs here
...
8 | assert_eq!(x, 1200);
| ^^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here
9 | println!("{} {} {}", x, y, z);
| - mutable borrow later used here
|
= note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
@LeoniePhiline I edited my previous message, now example works.
@bm0 It works because z
goes out of scope before y
is mutated.
Here’s what happens if you keep z
alive:
@LeoniePhiline it's called freezing, isn't it? This topic isn't covered in the rust book.
@bm0
No, it's called lifetimes.
Quote:
The lifetime (sometimes called a borrow) is alive from the place it is created to its last use.
Source: https://doc.rust-lang.org/nomicon/lifetimes.html#the-area-covered-by-a-lifetime
Read the subchapter "The area covered by a lifetime", and especially check out the example at the bottom, starting with:
And a lifetime can have a pause in it.
This is explaining exactly your scenario.
You could say, in your example:
-
x
's lifetime is'a
, -
y
's lifetime is'b
, and -
z
's lifetime is'c
.
In your example, 'c
lasts only this long:
'c: {
let z = &mut *y;
// y and z is mutable references to x at the same time, it isn't? <-- No:
// --> `'b`, the lifetime of `y` as mutable borrow of `x`, is "paused" here.
// Because in these lines you only mutate `x` through `z` and not through `y`.
*z += 1000;
}
At the end of this block (which does not need lifetime annotations, due to Lifetime Elision), z
as mutable borrow goes out of scope, allowing you to mutate z
in the following line (within lifetime 'b
).
@LeoniePhiline thanks a lot!
@bm0 Very welcome :)
I think the intent of the original post was lost a bit. I'm a beginner going through the rustlings exercises and I was completely derailed by this exercise because I had no clue what the *
operator was. Like @dtaralla said, the linked documentation
in the hint
text simply says.
We’ll see some uses of the dereference operator in Chapter 8 and discuss details of dereferencing in Chapter 15.
It seems backwards to require an understanding of a concept from Chapter 15 to complete an exercise meant to teach the basics of ownership.
I found this thread in a google search looking for an explanation for this exercise and the *
operator. Thanks @LeoniePhiline for the break down, it was very helpful
I think the intent of the original post was lost a bit. I'm a beginner going through the rustlings exercises and I was completely derailed by this exercise because I had no clue what the
*
operator was. Like @dtaralla said, the linked documentation in thehint
text simply says.We’ll see some uses of the dereference operator in Chapter 8 and discuss details of dereferencing in Chapter 15.
It seems backwards to require an understanding of a concept from Chapter 15 to complete an exercise meant to teach the basics of ownership.
I found this thread in a google search looking for an explanation for this exercise and the
*
operator. Thanks @LeoniePhiline for the break down, it was very helpful
I am in exactly the same position as you. Even if the solution doesn't require you to really understand dereferencing to solve it, it's a wholly unnecessary distraction. Especially with the compiler throwing an error on the line with the * operator, you are led to believe you need to know something about how it works in order to fix the error. This is badly written- if it's really supposed to be for beginners.
I personally found that it nicely makes beginners dive into research to proactively find answers themselves. It’s a great way to discover the rust book and other documentation.
If you use the usual rustlings hint (contents at around line 220) you are led to the References and borrowing page.
This page provides the solution:
Note: The opposite of referencing by using
&
is dereferencing, which is accomplished with the dereference operator,*
.
That doesn't actually provide the solution, though? The solution is to re-order the existing code.
The instructions on that tutorial page, the instructions in the readme, and the hint all provide conflicting information as to what is important about solving that lesson. The lesson isn't one on research or documentation, it's a single lesson in a set of lessons that are all about the concept of passing ownership around a program.
Learning to navigate the rust docs is undoubtedly an important skill, but that's clearly not the intent here.