2017-10-06 60 views
2

我有以下代码编译:为什么接受Box <MyType>的函数会在接受自我的函数有效时移动值?

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

我觉得代码OP *想*后为[这样的事情(https://play.rust-lang.org/?gist=39776f2b5554c07a764ebda48939d2dc&version=stable)。 – user4815162342

+2

锈标准命名说,你的模块应该是snake_case('btree'),你的类型应该是PascalCase('Node')。你的变量不应该有尾部下划线('node_')。你的一生('<'a>')没有效果,应该被删除。 – Shepmaster

回答

2

如果你手头有结构本身,你只能解构一个结构体(移动它​​的非Copy元素)。有一个指向堆上结构的指针是不够的,即使它是一个拥有指针(例如Box)。 Shepmaster's answer更详细地描述了为什么是这种情况。

幸运的是,node.leftOption<_>,所以有一个简单的解决方法: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) 
     } 
    } 
} 

Playground.

(我已经改名为BtreeNode遵循锈命名规则和删除<'a>寿命。)

2

Box是非常特殊的防锈;它是众所周知的编译器和某些技巧发生。例如,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>> { /* ... */ } 

因为整个结构已经转移到功能,可以安全地把它拆开,并具有无效结构成员。当你这样做时,编译器跟踪哪些成员是有效的,哪些不是为了正确地调用(或不调用)它们的析构函数。


在第二种形式中,整个Box<Node>被传递给函数。当您尝试修改该结构的成员时,您正尝试进入该结构并删除成员的所有权。您不可以获得您只能参考的东西的所有权 - 无法通知真实的该参考的所有者哪些片段有效,哪些不是。

在这种情况下,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::takereplaces 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)) 
} 
相关问题