Issue with reference

In the code below the example_1 function compiles, whereas example_2 fails to compile.

In principle the compiler could infer that the value of f is no longer used elsewhere, since f is set to None similar to example_1. Can someone explain why compiling example_2 fails nevertheless with an E0507?

enum Foo {
    Bar,
    Baz
}

pub fn example_1() {

    let mut f: Option<Foo> = Some(Foo::Bar);

    loop {
        match (1, &f) {
            (_, Some(_)) => {
                let g = f.unwrap();

                f = None
            },
            (_, None) => println!("None"),
        }

        if true { break }
    }

}

pub fn example_2() {

    let mut f: Option<Foo> = Some(Foo::Bar);

    loop {
        match (1, &f) {
            (_, Some(fr)) => {
                // error[E0507]: cannot move out of `*fr` which is behind a shared reference
                let g = *fr;

                f = None
            },
            (_, None) => println!("None"),
        }

        if true { break }
    }

}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
warning: unused variable: `g`
  --> src/lib.rs:14:21
   |
14 |                 let g = f.unwrap();
   |                     ^ help: if this is intentional, prefix it with an underscore: `_g`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `g`
  --> src/lib.rs:36:21
   |
36 |                 let g = *fr;
   |                     ^ help: if this is intentional, prefix it with an underscore: `_g`

error[E0507]: cannot move out of `*fr` which is behind a shared reference
  --> src/lib.rs:36:25
   |
36 |                 let g = *fr;
   |                         ^^^ move occurs because `*fr` has type `Foo`, which does not implement the `Copy` trait
   |
note: if `Foo` implemented `Clone`, you could clone the value
  --> src/lib.rs:1:1
   |
1  | enum Foo {
   | ^^^^^^^^ consider implementing `Clone` for this type
...
36 |                 let g = *fr;
   |                         --- you could clone this value
help: consider removing the dereference here
   |
36 -                 let g = *fr;
36 +                 let g = fr;
   |

For more information about this error, try `rustc --explain E0507`.
warning: `playground` (lib) generated 2 warnings
error: could not compile `playground` (lib) due to 1 previous error; 2 warnings emitted

You can't move out of a shared reference without copying the value behind the reference, because you don't have ownership over said value. Otherwise you'd have two owners which is not permitted by Rust's ownership rules. Only owned resources can be moved.

If you have a variable that you can get a mutable reference to, you can mem::take the value from it and replace it with the default value (None in your example) to avoid violating ownership rules:

    match f {
        Some(_) => {
            let g = std::mem::take(&mut f);
        },
        None => println!("None"),
    }

Playground.

3 Likes

Thanks, that makes sense :slight_smile:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.