Beginner Q: as_mut() and unwrap()

Hi! I'm starting out with Rust and am struggling to understand exactly what's going on here.

The error I'm getting is:

error[E0506]: cannot assign to `h.next` because it is borrowed
  --> src/list.rs:62:5
   |
57 |     while let Some(k) = h.next.as_mut() {
   |                         ------ borrow of `h.next` occurs here
...
62 |     h.next = Some(node);
   |     ^^^^^^
   |     |
   |     assignment to borrowed `h.next` occurs here
   |     borrow later used here

However, if I replace the while let block with:

while let Some(_) = h.next.as_mut() {
  h = h.next.as_mut().unwrap();
  ind += 1;
}

I receive no errors. I don't understand how these two are different. Isn't h.next borrowed in both cases?

Additionally, if I remove the assignment to h inside the while loop, no errors are thrown.

Here's the complete code:

struct Node<'a, T> {
  next: Option<Box<Node<'a, T>>>,
  item: &'a T,
}

pub struct List<'a, T> {
  head: Option<Box<Node<'a, T>>>,
}

impl<'a, T> List<'a, T> {
  pub fn new() -> Self {
    Self {
      head: None,
    }
  }

  pub fn insert(&mut self, item: &'a T) {
    let node = Box::new(Node::new(item));

    if let None = self.head {
      self.head = Some(node);
      return;
    }

    let mut h = self.head.as_mut().unwrap();
    let mut ind = 1;

    while let Some(k) = h.next.as_mut() {
      h = k;
      ind += 1;
    }

    h.next = Some(node);
  }
}

impl<'a, T> Node<'a, T> {
  fn new(item: &'a T) -> Self {
    Self {
      next: None,
      item,
    }
  }
}

An explanation of what's going on here would be amazing. Please let me know if I can clarify anything further. Thanks!

The code you have posted does not compile because the List struct is missing.

So this is kinda tricky, and is honestly just a limitation of the borrow checker. It's basically because the h.next.as_mut() expression must borrow h for equally long no matter how the value is used (the borrow has just one lifetime), and in one case you assign the resulting value k back to h. Thus the borrow must be long enough to last the full lifetime of h itself.

The question is now what happens when the option is None. In the version that fails, you still borrow it for that long, and hence it is still borrowed when you assign to next. On the other hand, in the version that works, the borrow that checks whether it is some is very short, and does not have to extend all the way down to the end.

Note that if instead of assigning to next through h.next, you assign through the borrow you made in the loop, then it is fine because the problem is that you borrowed h again after having borrowed it for a long time to check whether it is some.

loop {
    match &mut h.next {
        Some(k) => {
            h = k;
            ind += 1;
        }
        next => {
            *next = Some(node);
            break;
        }
    }
}

Thanks for the reply!

This is because we're borrowing from h, right? Even though we're binding to k, the reference is still valid outside the loop scope. So in the case where there is some value, we're reassigning to h. However, when there's no value, I guess we do borrow h.next but I don't see why that borrow matters?

EDIT:

Okay I think I understand now. The borrow inside the loop has a lifetime that is equal to the lifetime of h? This is because of how the borrow checker uses the elision rules in this case, right?

Thus, since the borrow lasts longer than the loop scope, we cannot assign to h.next because the borrow checker thinks we have a usable reference to h.next? But in this case we don't really. If this is the case, then your first statement is very much true, this really does seem like a big limitation.

The accepted answer here was also helpful in understanding some of this.

Would love to make sure I have the right train of thought here. Any further clarification you can provide would be greatly appreciated!

Fixed, sorry about that.