`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.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.