#method taking `&mut self`, returning immutable reference

4 messages · Page 1 of 1 (latest)

median jackal
#

Trying to compile this code:

struct Foo {
    foo: i32,
    bar: String
}

impl Foo {
    fn take_mut_ret_immut(&mut self) -> &String {
        self.foo = 4;
        &self.bar
    }
}

fn main() {
    let mut foo = Foo {
        foo: 2,
        bar: "bar".into(),
    };
    
    let bar = foo.take_mut_ret_immut();
    let _ = &foo.foo;
    println!("{bar}");
}

...yields this error:

error[E0502]: cannot borrow `foo.foo` as immutable because it is also borrowed as mutable
  --> src/main.rs:21:13
   |
20 |     let bar = foo.take_mut_ret_immut();
   |               --- mutable borrow occurs here
21 |     let _ = &foo.foo;
   |             ^^^^^^^^ immutable borrow occurs here
22 |     println!("{bar}");
   |               ----- mutable borrow later used here

For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` (bin "playground") due to 1 previous error

..but why? Why is bar a mutable borrow, even if Foo::take_mut_ret_immut clearly returns an immutable reference to Foo::bar?

runic steppe
#

Unfortunately, lifetimes can't be downgraded, so the borrow of foo is always mutable as long as bar exists. This is sometimes useful and required, like for Cell::from_mut, but it unfortunately doesn't allow your code to work. You can of course break them into two different functions, or you can have the function take a closure and give the closure &self.

#

?play

struct Foo {
    foo: i32,
    bar: String,
}

impl Foo {
    fn take_mut_ret_immut<R>(&mut self, f: impl FnOnce(&Self, &String) -> R) -> R {
        self.foo = 4;
        f(self, &self.bar)
    }
}

fn main() {
    let mut foo = Foo {
        foo: 2,
        bar: "bar".into(),
    };

    foo.take_mut_ret_immut(|foo, bar| {
        let _ = &foo.foo;
        println!("{bar}");
    });
}
lavish sundialBOT
#
bar```