Rust模式匹配不获取所有权使用值解决E0382报错
问题背景
我正在学习too-many-linked-lists教程,尝试实现一个简单链表,初始代码如下:
use std::mem; struct Node{ elem: i32, next : Link, } enum Link { Empty, More(Box<Node>), } pub struct List{ head: Link, } impl List{ pub fn pop_node(&mut self) -> Link{ let node = mem::replace(&mut self.head, Link::Empty); match node { Link::More(nd) => {self.head= nd.next;} _ => () }; node } }
pop_node函数的设计目标是返回链表的头节点,但上述代码无法通过编译,编译器报错提示访问nd时发生了变量node的移动。具体编译错误如下:
error[E0382]: use of partially moved value: `node` --> src/first.rs:34:9 | 31 | Link::More(nd) => {self.head= nd.next;} | -- value partially moved here ... 34 | node | ^^^^ value used here after partial move | = note: partial move occurs because value has type `Box<Node>`, which does not implement the `Copy` trait help: borrow this field in the pattern to avoid moving `node.0` | 31 | Link::More(ref nd) => {self.head= nd.next;} | +++ For more information about this error, try `rustc --explain E0382`.
尝试过解包Node结构体等方案都没有生效,需要解决该编译问题。
错误原因
模式匹配写Link::More(nd)时,会将枚举变体内部包裹的Box<Node>所有权移动到nd变量上,导致外层的node变量发生部分移动。Rust所有权规则禁止使用被部分移动的值,因此最后返回node时会触发E0382错误。
仅按照编译器提示加ref拿到不可变引用也无法通过编译:一方面nd.next的赋值操作需要修改节点内部值,另一方面直接移动nd.next同样会导致nd被部分移动,连带外层node结构不完整,依然违反所有权规则。
修复方案
在模式匹配中使用ref mut获取节点的可变引用,同时配合mem::replace取出next字段值,避免节点内部发生部分移动,修复后的可运行代码如下:
use std::mem; struct Node{ elem: i32, next : Link, } enum Link { Empty, More(Box<Node>), } pub struct List{ head: Link, } impl List{ pub fn pop_node(&mut self) -> Link{ let mut node = mem::replace(&mut self.head, Link::Empty); match node { Link::More(ref mut nd) => { // 临时将当前节点的next替换为空,取出原next作为新的链表头 self.head = mem::replace(&mut nd.next, Link::Empty); } _ => () }; node } }
逻辑说明
ref mut nd的绑定方式只会拿到Box<Node>的可变引用,不会发生所有权转移,外层node始终持有完整所有权,可以正常作为返回值。- 用
mem::replace将nd.next替换为Link::Empty,既可以取出原next值赋值给新的链表头,也不会把next从节点中移走导致节点结构被破坏,从根源上避免了部分移动问题。
内容的提问来源于stack exchange,提问作者Devil
相关产品推荐
相关产品推荐

