`match` vs `if let` reference problem

I try to solve leetcode problem Remove Linked List Elements using rust, following is the problem:

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

impl Solution {
    pub fn remove_elements(mut head: Option<Box<ListNode>>, val: i32) -> Option<Box<ListNode>> {
        // IMPL CODE!
    }
}

first I use match clause and it can compile and succeed:

pub fn remove_elements(mut head: Option<Box<ListNode>>, val: i32) -> Option<Box<ListNode>> {
        let mut ptr = &mut head;
        
        loop {
            match ptr {
                None => break,
                Some(node) if node.val == val => {
                    *ptr = node.next.take();
                }
                Some(node) => {
                    ptr = &mut node.next;
                }
            }
        }
        
        return head;
    }

but when I try to simplify the code with while let or if let, it fails with mutably borrowed error message:

pub fn remove_elements(mut head: Option<Box<ListNode>>, val: i32) -> Option<Box<ListNode>> {
        let mut ptr = &mut head;
        
        while let Some(node) = ptr {
            if node.val == val {
                *ptr = node.next;
            } else {
                ptr = &mut node.next;
            }
        }
        
        return head;
    }

or

pub fn remove_elements(mut head: Option<Box<ListNode>>, val: i32) -> Option<Box<ListNode>> {
        let mut ptr = &mut head;
        
        loop {
            if let Some(node) = ptr {
                if node.val == val {
                    *ptr = node.next;
                } else {
                    ptr = &mut node.next;
                }
            }
        }
        
        return head;
    }

so what's the difference between if/while let and match when destructuring?

The second and third versions run into limitations of the current borrow-checker. This will be fixed in the future by the new borrow-checker implementation, Polonius.

You can compile your examples today with a nightly rustc build by using the experimental -Zpolonius flag. (You'll need to add a .take() like in the first version, and add a missing break to the third version.)

1 Like

@mbrubeck,

what confuses me is that I think the first one should NOT work too as with the second using current stable version of rustc.

More clearly,

	match ptr { // ptr <- `&mut Option<T>`
		Some(node) => {
		// `Some(node)` refers to `&mut Option<T>`,
		// so `node` should be `&mut T`,
		// `node` mutablly refers to `ptr` hence both `*ptr` or `ptr=` should not work.
			ptr = ...;
			*ptr = ...;
		}
		......
	}

but it compiles, while in the second while variant it fails, could you explain to me why?

Thanks.