不能借用 <var> 作为不可变的,因为它也被借用为可变的不可变借用发生在这里

cannot borrow <var> as immutable because it is also borrowed as mutable immutable borrow occurs here

学习一些 Rust 编程(Leetcode Q3)。

收到错误:

cannot borrow `result` as immutable because it is also borrowed as mutable
immutable borrow occurs hererustcE0502
solution.rs(42, 23): mutable borrow occurs here
solution.rs(44, 13): mutable borrow later used here

我可以将程序从 if result.is_none() { 更改为 if current.is_none() {,它会起作用。但是当前和结果不是一回事。当前是 linked 列表中的最后一个节点,结果是头。我该怎么做?

Rust 游乐场link:https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=741c07fad8aa730a44ba5773395aed7c

// 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
    }
  }
}

#[allow(dead_code)]
struct Solution;

#[allow(dead_code)]
impl Solution {
    pub fn add_two_numbers(l1: Option<Box<ListNode>>, l2: Option<Box<ListNode>>) -> Option<Box<ListNode>> {
        let mut carry = 0;
        let mut result: Option<Box<ListNode>> = None;
        let mut current: &mut Option<Box<ListNode>> = &mut None;
        let mut node1 = &l1;
        let mut node2 = &l2;

        while node1.is_some() || node2.is_some() || carry > 0 {
          if node1.is_some() {
            carry += node1.as_ref().unwrap().val;
            node1 = &node1.as_ref().unwrap().next;
          }
          if node2.is_some() {
            carry += node2.as_ref().unwrap().val;
            node2 = &node2.as_ref().unwrap().next;
          }

          if result.is_none() { // solution.rs(42, 23): mutable borrow occurs here
            result = Some(Box::new(ListNode::new(carry % 10)));
            current = &mut result; // solution.rs(44, 13): mutable borrow later used here
          } else {
            current.as_mut().unwrap().next = Some(Box::new(ListNode::new(carry % 10)));
            current = &mut current.as_mut().unwrap().next;
          }
          carry = carry / 10;
        }
        return result;
    }
}


#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn it_works() {
        fn from_vec(l: Vec<i32>) -> Option<Box<ListNode>> {
          let mut start = None;
          let mut current: &mut Option<Box<ListNode>> = &mut None;
          for x in l {
            if current.is_none() {
              start = Some(Box::new(ListNode::new(x)));
              current = &mut start;
            } else {
              current.as_mut().unwrap().next = Some(Box::new(ListNode::new(x)));
              current = &mut current.as_mut().unwrap().next;
            }
          }
          return start;
        }
        fn compute(l1: Vec<i32>, l2: Vec<i32>) -> Vec<i32> {
          let result = Solution::add_two_numbers(from_vec(l1), from_vec(l2));
          let mut node = &result;
          let mut result_vec = Vec::new();
          while node.is_some() {
            result_vec.push(node.as_ref().unwrap().val);
            node = &node.as_ref().unwrap().next;
          }
          return result_vec;
        }
        assert_eq!(compute(vec![2, 4, 3], vec![5, 6, 4]), vec![7, 0, 8]);
        assert_eq!(compute(vec![0], vec![0]), vec![0]);
        assert_eq!(compute(vec![9, 9, 9, 9, 9, 9, 9], vec![9, 9, 9, 9]), vec![8, 9, 9, 9, 0, 0, 0, 1]);
    }
}

众所周知,由于这些别名问题,链表很难在 Rust 中实现。根本问题是你不能对 result 拥有的东西进行可变引用(即使是间接引用),然后在该引用处于活动状态时再次使用 result

问题的症结在于您检查 result 是否有值——换句话说,检查结果列表是否为空。这不起作用,因为在循环的第二次迭代中,current 引用 result,因此 result 无法访问,因为它已被完全借用。

解决此问题的最简单方法是通过更改 current 点的位置来取消对 result.is_some() 的检查。

不是引用列表中可能存在也可能不存在的最后一个节点,而是引用下一个要填充的节点——即,末尾的 None -- 总是 存在于 某处。

let mut result: Option<Box<ListNode>> = None;
let mut current: &mut Option<Box<ListNode>> = &mut result;

现在您不需要检查 result 因为 current 总是 指向您应该放置下一个节点的位置。这可能在 result 或节点的 next 字段中,但您不必关心它。

您可以将 if/else 块替换为:

*current = Some(Box::new(ListNode::new(carry % 10)));
current = &mut current.as_mut().unwrap().next;

这是有效的,因为在它的声明和你从函数中 return 它的那一刻你没有访问 result。当你 return 它时,current 引用不再被认为是活动的,因为 non-lexical lifetimes.

(Playground)