2

I'm having trouble understanding the difference in the two examples are and why the second doesn't work? I can't assign tho list.next in the second because it's borrowed as mutably.

struct List<T> {
    value: T,
    next: Option<Box<List<T>>>,
}

fn append_last<T>(mut list: &mut List<T>, append: List<T>) {
    while let Some(ref mut next) = list.next {
        list = next;
    }
    list.next = Some(Box::new(append));
}

fn append_last2<T>(mut list: &mut List<T>, append: List<T>) {
    while let Some(next) = list.next.as_mut() {
        list = next;
    }
    list.next = Some(Box::new(append));
}
fn append_last3<T>(mut list: &mut List<T>, append: List<T>) {
    while let Some(next) = list.next {
        list = &mut next;
    }
    list.next = Some(Box::new(append));
}

Why does ref mut only borrow inside the loop (when expanded match statement essentially) but the .as_mut() or &mut list.next would borrow till the end of the function essentially prohibiting me from assigning list.next at the end? Shouldn't the borrow end after the while let Some?And shouldn't the third example be the same as the first? Here it says that I can't move list.next out of the &mut list (obviously) because I' really matching on the value directly ... but isn't this exactly what I'm doing in the first example aswell? What am I missing here? error message is

error[E0506]: cannot assign to `list.next` because it is borrowed
   --> src/main.rs:176:5
    |
172 | fn append_last<T>(mut list: &mut List<T>, append: List<T>) {
    |                             - let's call the lifetime of this reference `'1`
173 |     while let Some(next) = list.next.as_mut() {
    |                            ------------------
    |                            |
    |                            `list.next` is borrowed here
    |                            argument requires that `list.next` is borrowed for `'1`
...
176 |     list.next = Some(Box::new(append));
    |     ^^^^^^^^^ `list.next` is assigned to here but it was already borrowed
Domasch
  • 21
  • 2
  • Please post the full error from `cargo check`. – Chayim Friedman Aug 15 '23 at 15:31
  • This is a known bug in the borrow checker. To be honest I'm quite surprised the first version works. – Chayim Friedman Aug 15 '23 at 15:33
  • 2
    @ChayimFriedman I'd use the word "limitation" rather than "bug", "bug" kinda implies nobody has come around to fix it. – Masklinn Aug 15 '23 at 15:45
  • I added the full error message. So this is a "limitation" of the borrow checker? I read some things about it regarding NLL and the Polonius change that's supposed to fix this... but how are the first and second things actually different? Is it the same like I suppose or does ref mut actually do something different? – Domasch Aug 15 '23 at 15:57
  • The two are actually almost verbatim the same (see [`Option::as_mut`](https://doc.rust-lang.org/src/core/option.rs.html#695)); the difference lies in that one of the two versions has a method call IMO. – jthulhu Aug 15 '23 at 16:21
  • I also tried inlining as_mut() but this also didn't work so I don't think it's the method call? Also wouldn't the first example also move list.next? The third example I provided shows exactly my worry I had that I can't move list.next out behind the &mut. – Domasch Aug 15 '23 at 16:32
  • By the way, you should always double-check when adding new code to your question. Your `append_last3` code has typos (`Last` -> `List`). But the original error has that same typo anyway and mismatched `append_last` and `append_last2`. – kmdreko Aug 15 '23 at 18:53
  • @kmdreko noted and fixed thx for the comment! – Domasch Aug 15 '23 at 19:39
  • 1
    The difference is that `as_mut()` borrows the `Option` mutably, and `ref mut` does not. `while let Some(next) = &mut list.next` fails with the same error. – Chayim Friedman Aug 15 '23 at 20:14
  • @ChayimFriedman btu if ref mut does not borrow the option then it would need to move it right? Does destructing into the reference of the inner value not destroy the original Some()? Because if i just use if let Some(val) = list.next as an example I move it behind reference which is obviously not allowed. using the ref keyword on the other hand doesn't throw that error meaning the Option is not moved .. meaning it hast to be "borrowed"?? Where is the mistake in my thought? – Domasch Aug 16 '23 at 19:58
  • Okay I just checked and if I match an enum or Struct by value and match ALL of the insides with an identifier pattern (ref or ref mut) the whole thing is not moved. ```rust let p = Person { fname: String::from("hans"), lname: String::from("hallo"), }; let Person { ref fname, ref lname, } = p;``` does not move p removing any of the refs there would So I guess the whole Option is never borrowed? – Domasch Aug 17 '23 at 08:47

0 Answers0