"Mutable borrow starts here in previous iteration of loop" on a Option Box value

Rust newb here. Saw another topic with the same title but the problem appear to be different. So sorry if I asked something that's already been answered.

Here's a code snippet that demonstrates the problem.

pub struct Solution {}

// Definition for singly-linked list.
#[derive(PartialEq, Eq, Clone, Debug)]
pub struct ListNode {
    pub val: i32,
    pub next: Option<Box<ListNode>>,
}

impl ListNode {
    #[inline]
    pub fn new(val: i32) -> Self {
        ListNode { next: None, val }
    }
}

impl Solution {
    pub fn iterate_through(head: Option<Box<ListNode>>) -> Option<Box<ListNode>> {
        if head.is_none() {
            return None;
        }
        let mut h = head;
        let mut p1 = h.as_mut().unwrap();

        while let Some(p2) = p1.next.as_mut() {
            if true {
               p1 = p2;
            }
        }
        h
    }
}

cargo check shows that:

while let Some(p2) = p1.next.as_mut() {
   |                              ^^^^^^^ mutable borrow starts here in previous iteration of loop

My question is, in the if statement in the while loop, we rebind p1 to p2 (i.e. p1.next) in every iteration. This means that at the beginning of the next iteration, p1.next on the while let line should point to a different ListNode in each iteration, right? So why does rustc think that p1.next was borrow multiple times?

Weirdly, removing the if true statement satisfies the borrow checker:

while let Some(p2) = p1.next.as_mut() {
      p1 = p2;
}

Thanks in advance!

1 Like

The borrow checker does not care that you put if true, it will do the same as if you put if some_bool. More generally, if the correctness of you program depends on the (possibly) runtime state of your program, you will have to use a workaround to satisfy the borrow checker.

Thanks Krishna it makes sense that we should give the borrow checker enough information to determine the safety of the program. But I'm still confused on why it complained about "mutable borrow starts here in previous iteration" in the first place. Could you elaborate on that?

I suppose it can be boiled down to the fact that when I place p1=p2 inside an if statement, it may or may not happen so p1.next may be borrowed twice?

1 Like

Yes, this seems to be why. branch analysis is hard so Rust can get tripped up really easily.

This is what makes it difficult for me to write rust sometimes. I just assume that the rust compiler and borrow checker are right always. This makes me wonder if my code is right when the compiler complains, where actually the compiler is just being conservative in its judgement and can't analyse the code in the best way

Rust prefers false negatives over false positives. This inherently means that Rust cannot accept all correct programs (which is why we need unsafe). This is better than accepting some wrong programs because that would make the entire thing unreliable. But the compiler is getting better, and with the next borrow checker, polonius, it should be able to handle branches better.

1 Like