Checking chain_table v0.1.0 (/home/ooj2003/VSCodeProjects/rust/chain_table) error[E0507]: cannot move out of `self.head` which is behind a mutable reference --> src/main.rs:33:19 | 33 | next: self.head, | ^^^^^^^^^ move occurs because `self.head` has type `Option<Box<Node<i32>>>`, which does not implement the `Copy` trait | help: consider borrowing the `Option`'s content | 33 | next: self.head.as_ref(), | +++++++++
For more information about this error, try `rustc --explain E0507`. error: could not compile `chain_table` due to previous error
用 H 代表头节点,则原先的所有权关系大致如图(箭头代表持有所有权) \[
self :H \rightarrow a \rightarrow b \rightarrow \dots
\] 现在我要取出 a 并返回,首先用 mem::replace() 函数将a的持有物置换为 None 并将置换所得绑定到一个新变量 new 上,此时所有权结构如下 \[
self: H, \quad new: a\rightarrow b \rightarrow\dots
\] 注意H的类型签名是 List ,a、b等小写字母(表示节点)的类型签名是 Node,
接下来用 Option 包装并返回 a 的 elem 字段内容物,并将 a 的 next 字段内容物的所有权移交给 H ,此时所有权结构如下。 \[
self: H \rightarrow b \rightarrow \dots
\] 此时 a 和 new 在完成操作后生命周期结束并自动调用析构函数被销毁。
对于所有基本类型,rust 都已经实现了 Drop trait 即析构函数,自定义的结构体则会在生命周期结束时递归的为每个字段调用 drop 方法。当面对链表时,自动生成的析构函数则会递归的去找到表尾并销毁,然而是表尾前一个元素,然后再前一个,以此类推。
如果这个链表非常的长的话,不出预料的,程序会爆栈,所以我们要手动的为我们的链表实现 Drop trait 。
1 2 3 4 5 6 7 8 9 10 11
implDropforList { fndrop(&mutself) { letmut a = std::mem::replace(&mutself.head, None); loop { match a { None => return, Some(mut x) => a = std::mem::replace(&mut x.next, None), } } } }
首先用 replace() 函数将 self 的 head 字段用 None 替换,返回值绑定到变量 a 上,然后对 a 进行模式匹配,如果是 None 则表示链表为空或者已经匹配到表尾的 next 字段。如果不是, 则将匹配项的 next 字段重新绑定到 a 上, 注意这时原先 a 持有的内容物在这一操作后生命周期已经结束,编译器会自动为其调用 drop() 方法。这么一番操作下来,函数调用栈就不会被递归撑爆了。