Borrow, match, and reference return

Hello everybody,

I'm stuck when I try to do the Closure exemple in the Rust Book.
https://doc.rust-lang.org/book/ch13-01-closures.html

I try to change the value function of the Cacher to allow it to return a reference of the value that is in the cache I did it with if/else statement with success:

impl<T,P,R> Cacher<T,P,R>
    where T : Fn(P) -> R
{
    fn value(&mut self, param: P) -> &R {
        if self.value.is_some() {
            self.value.as_ref().unwrap()
        }
        else {
            let v = (self.calculation)(param);
            self.value = Some(v);
            self.value.as_ref().unwrap()
        }
    }
}

I tried to do it with pattern matching but without success and I'm completly stuck.
I tried the following:

fn value<'a>(&'a mut self, param: P) -> &'a R {
        match &self.value {
            Some(ref x) => x,
            None => { 
                let v = (self.calculation)(param);
                self.value = Some(v);
                self.value.as_ref().unwrap()
            }
        }
    }

But I have 2 errors:

error[E0506]: cannot assign to `self.value` because it is borrowed
  --> src\main.rs:50:17
   |
45 |     fn value(&mut self, param: P) -> &R {
   |              - let's call the lifetime of this reference `'1`
46 |         match &mut self.value {
   |               --------------- borrow of `self.value` occurs here
47 |             Some(x) => x,
   |                        - returning this value requires that `self.value` is borrowed for `'1`
...
50 |                 self.value = Some(v);
   |                 ^^^^^^^^^^ assignment to borrowed `self.value` occurs here

error[E0502]: cannot borrow `self.value` as immutable because it is also borrowed as mutable
  --> src\main.rs:51:17
   |
45 |     fn value(&mut self, param: P) -> &R {
   |              - let's call the lifetime of this reference `'1`
46 |         match &mut self.value {
   |               --------------- mutable borrow occurs here
47 |             Some(x) => x,
   |                        - returning this value requires that `self.value` is borrowed for `'1`
...
51 |                 self.value.as_ref().unwrap()
   |                 ^^^^^^^^^^ immutable borrow occurs here

I don't understand if it's a borrow issue or a lifetime issue.. or both...
Can someone explains?
Thank you

Well I fix it by matching self.value instead of &self.value

fn value(&mut self, param: P) -> &R {
            match self.value {
                Some(ref x) => x,
                None => { 
                    let v = (self.calculation)(param);
                    self.value = Some(v);
                    self.value.as_ref().unwrap()
           }
       }

I though match taking ownership of the value because I did not ask a reference on self.value. So returning a reference on the value owned in the match will not compile due to temporary variable but it's not the case...

I understand is that borrow or owership do not happened in the match line but in the 'case' line for each bracket, that explains the need of Some(ref x) to obtain a reference on the Option value instead of taking ownership. It explains why if I do Some(x) => &x the compiler says:

error[E0515]: cannot return reference to local variable `x`
  --> src\main.rs:47:24
   |
47 |             Some(x) => &x,
   |                        ^^ returns a reference to data owned by the current function

Is it possible to do the same by matching with &mut self.value? I don't know how.

Thank you

  • Fail

    match self.value /*: Option<R>*/ {
        Some(x /*: R*/ ) => &x /*: &R*/, // good type, but returns borrow of local
    

We need to bind by reference

  • With match ergonomics

    match &self.value /*: &Option<R>*/ {
        Some(x /*: &R*/ ) => x /*: &R*/, // good type, all works well
    

    this works because it is actually sugar for:

    match &self.value /*: &Option<R>*/ {
        &Some(ref x /*bind by ref to a R => &R*/ ) => x /*: &R*/,
     // ^
     // |
     // +-- this dereferences `&self.value` to temporarily work on `self.value`;
     //     This is important, since `Some` / `None` is a discriminant of `Option`, not of `&Option`
    

    In other words, this is also equivalent to

  • Your solution

    match self.value /*: Option<R>*/ {
        Some(ref x /*bind by ref to a R => &R*/ ) => x /*: &R*/,
    
    • we have "simplified the ampersands"

Yes, and it is the same idea, but using ref mut instead of ref:

match self.value /*: Option<R>*/ {
    Some(ref mut x /*bind by ref mut to a R => &mut R*/ ) => x /*: &mut R*/,

or:

match &mut self.value /*: &mut Option<R>*/ {
    &mut Some(ref mut x /*bind by ref mut to a R => &mut R*/ ) => x /*: &mut R*/,

or, with match ergonomics:

match &mut self.value /*: &mut Option<R>*/ {
    Some(x /*: &mut R*/ ) => x /*: &mut R*/,
1 Like

Thank you very much for the clarification.
The major point I don't understand is with the 'None' branch.
What I understand is:

match &mut self.value /* Mutable borrow occurs here */{
           Some(x) => x, /* Return mutable borrow of self.value.'x' */
           None => { 
               let v = (self.calculation)(param);
               self.value = Some(v);  /* Not working because self.value is already borrowed */
               self.value.as_ref().unwrap() /* Same problem here */
           }
       }

Is there a way to retrieves the mutable borrow of self.value in 'None' branch?
I found a workaround but I need to borrow outside the match like this:

let mut_ref_of_value = &mut self.value;
match mut_ref_of_value {
      Some(x) => x,
      None => { 
           let v = (self.calculation)(param);
           *mut_ref_of_value = Some(v);
           mut_ref_of_value.as_ref().unwrap()
      }
}

Thank you

Ok after a deep research on the web I found this :

match &mut self.value {
    Some(x) => x,
    value_ref @ None => {
        let v = (self.calculation)(param);
        *value_ref = Some(v);
        value_ref.as_ref().unwrap()
     }
}

This is not very well documented, I found this in an old version of the book https://doc.rust-lang.org/1.5.0/book/patterns.html
It also appears here https://doc.rust-lang.org/reference/expressions/match-expr.html but is totally ignored in the documentation.

1 Like

For non-educational purpose, you can use Option::get_or_insert_with() for it.

fn value(&mut self, param: P) -> &R {
    self.value.get_or_insert_with(|| (self.calculation)(param))
}
1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.