2017-10-06 49 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)) 
} 
相關問題