我有以下代码编译:
pub mod Btree {
pub struct node {
pub id: u32,
pub red: bool,
pub left: Option<Box<node>>,
pub right: Option<Box<node>>,
}
impl<'a> node {
pub fn insert(mut node_: Option<Box<node>>, id: u32) -> Option<Box<node>> {
match node_ {
None => Some(Box::new(node {
id: id,
red: true,
left: None,
right: None,
})),
Some(x) => x.insert_(id),
}
}
pub fn insert_(mut self, id: u32) -> Option<Box<node>> {
self.left = node::insert(self.left, id);
Some(Box::new(self))
}
}
}
当我更改insert_()
以使用Box<node>
代替时:
pub mod Btree {
pub struct node {
pub id: u32,
pub red: bool,
pub left: Option<Box<node>>,
pub right: Option<Box<node>>,
}
impl<'a> node {
pub fn insert(mut node_: Option<Box<node>>, id: u32) -> Option<Box<node>> {
match node_ {
None => Some(Box::new(node {
id: id,
red: true,
left: None,
right: None,
})),
Some(x) => node::insert_(x, id),
}
}
pub fn insert_(mut node_: Box<node>, id: u32) -> Option<Box<node>> {
node_.left = node::insert(node_.left, id);
Some(node_)
}
}
}
我明白了:
error[E0382]: use of partially moved value: `node_`
--> src/main.rs:23:13
|
23 | node_.left = node::insert(node_.left, id);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^----------^^^^^
| | |
| | value moved here
| value used here after move
|
= note: move occurs because `node_.left` has type `std::option::Option<std::boxed::Box<Btree::node>>`, which does not implement the `Copy` trait
我不明白那是什么。代码非常相似,在这两种情况下都有一个举动。
答案 0 :(得分:2)
如果你手头有结构,你只能构造一个结构(将非Copy
元素移出它)。指向堆上结构的指针是不够的,即使它是一个拥有指针(例如Box
)。 Shepmaster's answer更详细地描述了为什么会出现这种情况。
幸运的是,node.left
是Option<_>
,因此可以轻松解决此问题:Option::take
。 .take()
上的Option
会为您提供内部值(如果有的话),但不会使用Option
,而是将None
放在其位置。因此,您可以在调用.take()
时暂时使用None
来交换Node::insert
,然后将其替换为返回值。
pub mod btree {
pub struct Node {
pub id: u32,
pub red: bool,
pub left: Option<Box<Node>>,
pub right: Option<Box<Node>>,
}
impl Node {
pub fn insert(node: Option<Box<Node>>, id: u32) -> Option<Box<Node>> {
match node {
None => Some(Box::new(Node {
id: id,
red: true,
left: None,
right: None,
})),
Some(x) => Node::insert_(x, id),
}
}
pub fn insert_(mut node: Box<Node>, id: u32) -> Option<Box<Node>> {
node.left = Node::insert(node.left.take(), id);
Some(node)
}
}
}
(我已重命名Btree
和Node
以遵循Rust命名约定并删除了<'a>
生命周期。)
答案 1 :(得分:2)
Box
在Rust中非常特别;它是众所周知的编译器和某些技巧发生。例如,you can move out of a box by dereferencing it。这与此有关。
当你的函数接受self
时,它等同于:
pub fn insert_(node: Node, id: u32) -> Option<Box<Node>>
Rust performs a number of dereference steps automatically,因此您的Box<Node>
dereferenced仅为Node
,然后可以调用该函数。因此,原始代码的扩展形式为:
Some(x) => Node::insert_(*x, id),
pub fn insert_(mut self: Node, id: u32) -> Option<Box<Node>> { /* ... */ }
因为整个结构已经转移到函数中,所以将它拆开并且具有无效的struct成员是安全的。执行此操作时,编译器会跟踪哪些成员有效以及哪些成员无法正确调用(或不调用)其析构函数。
在第二种形式中,整个Box<Node>
将传递给函数。当您尝试修改结构的成员时,您试图进入它并删除成员的所有权。您不能拥有您只引用的内容的所有权 - 无法通知引用的 true 所有者哪些部分有效,哪些部分无效。
在这种情况下,Box
不够特别,编译器无法处理此问题。
您可以通过显式移出Box
来复制编译器的功能。您还必须在返回时创建新的Box
:
Some(x) => Node::insert_(x, id),
pub fn insert_(node: Box<Node>, id: u32) -> Option<Box<Node>> {
let mut node = *node;
node.left = Node::insert(node.left, id);
Some(Box::new(node))
}
trentcl's answer以不同的方式解决了“可能无效,可能无效”的问题。 Option::take
replaces one valid value with another valid value。如果因任何原因需要运行析构函数,那么一切都将是安全且定义明确的。
您甚至可以use Box<...>
as the self
parameter type - Box
特别!
Some(x) => x.insert_(id),
pub fn insert_(self: Box<Node>, id: u32) -> Option<Box<Node>> {
let mut node = *self;
node.left = Node::insert(node.left, id);
Some(Box::new(node))
}