Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move_semantics5 makes use of "*", which has not been explained yet #809

Closed
dtaralla opened this issue Jul 25, 2021 · 13 comments
Closed

move_semantics5 makes use of "*", which has not been explained yet #809

dtaralla opened this issue Jul 25, 2021 · 13 comments

Comments

@dtaralla
Copy link

dtaralla commented Jul 25, 2021

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?

@bm0
Copy link

bm0 commented Aug 2, 2021

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);
}

@LeoniePhiline
Copy link

@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)

@bm0
Copy link

bm0 commented Aug 11, 2021

@LeoniePhiline I edited my previous message, now example works.

@LeoniePhiline
Copy link

@bm0 It works because z goes out of scope before y is mutated.

Here’s what happens if you keep z alive:

D3F73D9D-CBD4-4D27-99D8-1D6403472D62

@bm0
Copy link

bm0 commented Aug 11, 2021

@LeoniePhiline it's called freezing, isn't it?
This topic isn't covered in the rust book.

@LeoniePhiline
Copy link

LeoniePhiline commented Aug 11, 2021

@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).

@bm0
Copy link

bm0 commented Aug 11, 2021

@LeoniePhiline thanks a lot!

@LeoniePhiline
Copy link

@bm0 Very welcome :)

@PerezJD
Copy link

PerezJD commented Aug 25, 2021

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

@boabdilperez
Copy link

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 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.

@LeoniePhiline
Copy link

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, *.

@boabdilperez
Copy link

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.

@mo8it
Copy link
Contributor

mo8it commented Jul 10, 2024

Fixed in d7024d8

@mo8it mo8it closed this as completed Jul 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants