使用可变引用迭代递归结构并返回最后一个有效引用

时间:2018-02-04 16:50:41

标签: reference rust mutable borrowing

我试图递归一个节点结构,修改它们,然后返回我得到的最后一个returns。我使用example in the non-lexical lifetimes RFC解决了循环中可变引用的问题。如果我尝试将可变引用返回到最后一个Node,则会出现Node错误:

use of moved value

给出此错误

#[derive(Debug)]
struct Node {
    children: Vec<Node>,
}

impl Node {
    fn new(children: Vec<Self>) -> Self {
        Self { children }
    }
    fn get_last(&mut self) -> Option<&mut Node> {
        self.children.last_mut()
    }
}

fn main() {
    let mut root = Node::new(vec![Node::new(vec![])]);

    let current = &mut root;

    println!("Final: {:?}", get_last(current));
}


fn get_last(mut current: &mut Node) -> &mut Node {
    loop {
        let temp = current;
        println!("{:?}", temp);

        match temp.get_last() {
            Some(child) => { current = child },
            None => break,
        }
    }

    current
}

如果我返回临时值而不是中断,则会收到错误error[E0382]: use of moved value: `*current` --> test.rs:51:5 | 40 | let temp = current; | ---- value moved here ... 51 | current | ^^^^^^^ value used here after move | = note: move occurs because `current` has type `&mut Node`, which does not implement the `Copy` trait

cannot borrow as mutable more than once
fn get_last(mut current: &mut Node) -> &mut Node {
    loop {
        let temp = current;
        println!("{:?}", temp);

        match temp.get_last() {
            Some(child) => { current = child },
            None => return temp,
        }
    }
}

如何使用可变引用遍历结构并返回最后一个error[E0499]: cannot borrow `*temp` as mutable more than once at a time --> test.rs:47:28 | 43 | match temp.get_last() { | ---- first mutable borrow occurs here ... 47 | None => return temp, | ^^^^ second mutable borrow occurs here 48 | } 49 | } | - first borrow ends here ?我已经搜索过了,但是我找不到针对这个特定问题的任何解决方案。

我无法使用Obtaining a mutable reference by iterating a recursive structure,因为它让我多次借用错误:

Node

1 个答案:

答案 0 :(得分:3)

这确实与Cannot obtain a mutable reference when iterating a recursive structure: cannot borrow as mutable more than once at a time不同。如果我们查看那里的答案,修改了一下,我们可以看到它匹配一个值,并且能够返回终端案例中匹配的值。也就是说,返回值是Option

fn back(&mut self) -> &mut Option<Box<Node>> {
    let mut anchor = &mut self.root;

    loop {
        match {anchor} {
            &mut Some(ref mut node) => anchor = &mut node.next,
            other => return other, // transferred ownership to here
        }
    }
}

您的案件因两个方面而变得复杂:

  1. 缺少non-lexical lifetimes
  2. 你想要一个可变的引用并且&#34;放弃它&#34;在一个案例中(有孩子)而在另一个案例中没有(没有孩子)。这在概念上与此相同:

    fn maybe_identity<T>(_: T) -> Option<T> { None }
    
    fn main() {
        let name = String::from("vivian");
    
        match maybe_identity(name) {
            Some(x) => println!("{}", x),
            None => println!("{}", name),
        }
    }
    

    编译器无法判断None案例(非常理论上)可以继续使用name

  3. 直接的解决方案是对此进行编码&#34;将其取回&#34;行动明确。我们创建一个枚举,在没有子节点的情况下返回&mut self

    enum LastOrNot<'a> {
        Last(&'a mut Node),
        NotLast(&'a mut Node),
    }
    
    impl Node {
        fn get_last_or_self(&mut self) -> LastOrNot {
            match self.children.is_empty() {
                false => LastOrNot::Last(self.children.last_mut().unwrap()),
                true => LastOrNot::NotLast(self),
            }
        }
    }
    

    然后可以重写该函数以使用枚举:

    fn get_last(mut current: &mut Node) -> &mut Node {
        loop {
            match { current }.get_last_or_self() {
                LastOrNot::Last(child) => current = child,
                LastOrNot::NotLast(end) => return end,
            }
        }
    }
    

    请注意,我们正在使用Rust borrow of a HashMap lasts beyond the scope it's in?Cannot obtain a mutable reference when iterating a recursive structure: cannot borrow as mutable more than once at a time中公开的所有技术。

    使用NLL,我们可以简化get_last_or_self以避免布尔值:

    fn get_last_or_self(&mut self) -> LastOrNot {
        match self.children.last_mut() {
            Some(l) => LastOrNot::Last(l),
            None => LastOrNot::NotLast(self),
        }
    }
    

    通过正在进行的NLL重新实现,整个问题可以简化为非常简单形式:

    fn get_last(mut current: &mut Node) -> &mut Node {
        while let Some(child) = current.get_last() {
            current = child;
        }
    
        current
    }
    

    另见: