当匹配枚举时,无法移动借用内容

6
我尝试打印一棵树(现在是一个链表,但这将被修复):
use std::io;
use std::rc::Rc;

enum NodeKind {
    Branch(Rc<Node>),
    Leaf,
}

struct Node {
    value: i32,
    kind: NodeKind,
}

fn main() {
    let leaf = Node { value: 10, kind: NodeKind::Leaf };
    let branch = Node { value: 50, kind: NodeKind::Branch(Rc::new(leaf)) };
    let root = Node { value: 100, kind: NodeKind::Branch(Rc::new(branch)) };

    let mut current = root;
    while true {
        println!("{}", current.value);
        match current.kind {
            NodeKind::Branch(next) => {
                current = *next;
            }
            NodeKind::Leaf => {
                break;
            }
        }
    }

    let mut reader = io::stdin();
    let buff = &mut String::new();
    let read = reader.read_line(buff);
}

编译器提示:
error[E0507]: cannot move out of borrowed content
  --> src/main.rs:24:27
   |
24 |                 current = *next;
   |                           ^^^^^ cannot move out of borrowed content

我只是读取值,不做任何更改。我正在将一个引用的值赋给另一个值,尝试解引用一个 Rc<T> 值并将其存储在本地的 mut 变量中。

也许像这样做可以行:

while true {
    println!("{}", current.value);
    match &current.kind {
        &NodeKind::Branch(next) => {
            current = next;
        }
        &NodeKind::Leaf => {
            break;
        }
    }
}

或许
let mut current = &Rc::new(root);
while true {
    println!("{}", current.value);
    match current.kind {
        NodeKind::Branch(next) => {
            current = &next;
        }
        NodeKind::Leaf => {
            break;
        }
    }
}

但我得到了相同的错误,加上'next' does not live long enough

3个回答

8

这里没有必要克隆,使用引用是完全可以实现你想要的目标的:

use std::rc::Rc;

enum NodeKind {
    Branch(Rc<Node>),
    Leaf,
}

struct Node {
    value: i32,
    kind: NodeKind,
}

fn main() {
    let leaf = Node { value: 10, kind: NodeKind::Leaf };
    let branch = Node { value: 50, kind: NodeKind::Branch(Rc::new(leaf)) };
    let root = Node { value: 100, kind: NodeKind::Branch(Rc::new(branch)) };

    let mut current = &root;
    loop {
        println!("{}", current.value);
        match current.kind {
            NodeKind::Branch(ref next) => {
                current = &**next;
            }
            NodeKind::Leaf => break,
        }
    }
}

你的代码中唯一重要的更改是匹配模式中的ref nextcurrent&Node类型。

ref模式通过引用绑定它们的变量,也就是说,next的类型为&Rc<Node>。要从中获取&Node,需要对其进行两次解引用以获取Node,然后再次引用以获取&Node。由于Deref coercion(解引用强制转换),也可以编写current = &next,编译器会自动为您插入适当数量的*

我还将while(true)更改为loop,因为它更符合惯用语,并帮助编译器理解您的代码。

在Rust中,所有类似树形结构的遍历都是这样完成的。ref模式允许不移出变量,这在仅需要读取数据时绝对必要。您可以在此处找到有关模式及其与所有权和借用的交互方式的更多信息。

3

出现错误是因为默认情况下 match 会执行一个移动操作。

值被移动后(即未被引用或调用带有self的方法),随后的调用将失败。您可能需要进行克隆,这是您的 structenum 都缺乏的属性。一旦您添加了这些(#[derive(Clone))并将 current = *next; 更改为 current = (*next).clone();,您的程序将再次正常工作!

use std::io;
use std::rc::Rc;

#[derive(Clone)]
enum NodeKind {
    Branch(Rc<Node>),
    Leaf,
}

#[derive(Clone)]
struct Node {
    value: i32,
    kind: NodeKind,
}

fn main() {
    let leaf = Node { value: 10, kind: NodeKind::Leaf };
    let branch = Node { value: 50, kind: NodeKind::Branch(std::rc::Rc::new(leaf)) };
    let root = Node { value: 100, kind: NodeKind::Branch(std::rc::Rc::new(branch)) };

    let mut current = root;
    while true {
        println!("{}", current.value);
        match current.kind {
            NodeKind::Branch(next) => {
                current = (*next).clone();
            }
            NodeKind::Leaf => {
                break;
            }
        }
    }

    let reader = io::stdin();
    let buff = &mut String::new();
    let read = reader.read_line(buff);
}

如果你使用 let mut current = &root,那么就可以避免使用 clone(),如下所示的 Vladimir 的响应 (Vladimir 的版本的 playpen)。

Playground


当然你可以在match子句中借用,但在你的情况下,你受到结构创建方式的限制。即使你借用了,你仍然需要通过Clone显式地或通过Copy隐式地克隆它。你似乎正在寻找的是遍历树的迭代器。 - Daniel Fath
也许我应该写成 let mut current = Rc::new(root),这样我想要复制的只有 Rc 和 i32,而不是整个结构体。我只想读取 Rc 地址,解引用它,复制 i32 并打印它,然后读取下一个节点的地址并将当前节点分配给该地址,直到遇到叶子节点为止。这里只涉及到 Rc 和 i32 的复制(这在标准库中已经实现了,包括 Rc)。 - Alex Zhukovskiy
我明白了,所以电流不需要是一个具体的值,而是一个引用。这改变了事情的情况。 - Daniel Fath

1
我还无法解决1)的问题,但我已经找到了2)的答案。
在顶部,你需要使用: use std::rc::Rc; 而不是 use std::rc;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接