use std::mem;pubstructList<T> { head:Link<T>, tail:Link<T>, // NEW!}typeLink<T> =Option<Box<Node<T>>>;structNode<T> { elem:T, next:Link<T>,}impl<T> List<T> {pubfnnew() -> Self {List { head:None, tail:None } }pubfnpush(&mut self, elem:T) {let new_tail =Box::new(Node { elem: elem,// When you push onto the tail, your next is always None next:None, });// swap the old tail to point to the new taillet old_tail = mem::replace(&mut self.tail, Some(new_tail));match old_tail {Some(mut old_tail) => {// If the old tail existed, update it to point to the new tail old_tail.next =Some(new_tail); }None=> {// Otherwise, update the head to point to it self.head =Some(new_tail); } } }}
> cargo builderror[E0382]:use of moved value: `new_tail`--> src/fifth.rs:38:38|26 |let new_tail = Box::new(Node { | -------- move occurs because `new_tail` has type `std::boxed::Box<fifth::Node<T>>`, which does not implement the `Copy` trait
...33 |let old_tail = mem::replace(&mut self.tail, Some(new_tail));|-------- value moved here...38| old_tail.next =Some(new_tail);|^^^^^^^^ value used here after move
使用 move value: new tail
没有实现 Copy,所以我们不能把它分配到两个位置。更重要的是,Box 拥有它所指向的东西,并且会在它 drop 时释放它。如果我们编译了 push 实现,我们就可以双倍释放列表的尾部了!实际上,正如我们所写的那样,我们的代码会在每次推送时释放 old_tail。哎呀!
好吧,我们知道如何制作一个非所有权指针。 那只是参考!
pubstructList<T> { head:Link<T>, tail:Option<&mutNode<T>>, // NEW!}typeLink<T> =Option<Box<Node<T>>>;structNode<T> { elem:T, next:Link<T>,}impl<T> List<T> {pubfnnew() -> Self {List { head:None, tail:None } }pubfnpush(&mut self, elem:T) {let new_tail =Box::new(Node { elem: elem,// When you push onto the tail, your next is always None next:None, });// Put the box in the right place, and then grab a reference to its Nodelet new_tail =match self.tail.take() {Some(old_tail) => {// If the old tail existed, update it to point to the new tail old_tail.next =Some(new_tail); old_tail.next.as_mut().map(|node|&mut**node) }None=> {// Otherwise, update the head to point to it self.head =Some(new_tail); self.head.as_mut().map(|node|&mut**node) } }; self.tail = new_tail; }}
pubstructList<'a, T> { head:Link<T>, tail:Option<&'amutNode<T>>, // NEW!}typeLink<T> =Option<Box<Node<T>>>;structNode<T> { elem:T, next:Link<T>,}impl<'a, T> List<'a, T> {pubfnnew() -> Self {List { head:None, tail:None } }pubfnpush(&mut self, elem:T) {let new_tail =Box::new(Node { elem: elem,// When you push onto the tail, your next is always None next:None, });// Put the box in the right place, and then grab a reference to its Nodelet new_tail =match self.tail.take() {Some(old_tail) => {// If the old tail existed, update it to point to the new tail old_tail.next =Some(new_tail); old_tail.next.as_mut().map(|node|&mut**node) }None=> {// Otherwise, update the head to point to it self.head =Some(new_tail); self.head.as_mut().map(|node|&mut**node) } }; self.tail = new_tail; }}
cargo builderror[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements--> src/fifth.rs:35:27|35| self.head.as_mut().map(|node|&mut**node)|^^^^^^|note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 18:5...--> src/fifth.rs:18:5|18|/pubfnpush(&mut self, elem:T) {19||let new_tail =Box::new(Node {20|| elem: elem,21||// When you push onto the tail, your next is always None...|39|| self.tail = new_tail;40|| }||_____^note:...so that reference does not outlive borrowed content--> src/fifth.rs:35:17|35| self.head.as_mut().map(|node|&mut**node)|^^^^^^^^^note: but, the lifetime must be valid for the lifetime 'aas defined on the impl at 13:6...--> src/fifth.rs:13:6|13|impl<'a, T> List<'a, T> {|^^= note:...so that the expression is assignable: expected std::option::Option<&'amut fifth::Node<T>> found std::option::Option<&mut fifth::Node<T>>
cargo build
warning: field is never used: `elem`
--> src/fifth.rs:9:5
|
9 | elem: T,
| ^^^^^^^
|
= note: #[warn(dead_code)] on by default
哦,嘿,成功了! 太好了!
让我们也做一下 pop 吧:
pubfnpop(&'amut self) ->Option<T> {// Grab the list's current head self.head.take().map(|head| {let head =*head; self.head = head.next;// If we're out of `head`, make sure to set the tail to `None`.if self.head.is_none() { self.tail =None; } head.elem })}
并为此写一个快速测试:
mod test {use super::List; #[test]fnbasics() {letmut list =List::new();// Check empty list behaves rightassert_eq!(list.pop(), None);// Populate list list.push(1); list.push(2); list.push(3);// Check normal removalassert_eq!(list.pop(), Some(1));assert_eq!(list.pop(), Some(2));// Push some more just to make sure nothing's corrupted list.push(4); list.push(5);// Check normal removalassert_eq!(list.pop(), Some(3));assert_eq!(list.pop(), Some(4));// Check exhaustionassert_eq!(list.pop(), Some(5));assert_eq!(list.pop(), None); }}
cargo testerror[E0499]: cannot borrow `list` as mutable more than once at a time--> src/fifth.rs:68:9|65|assert_eq!(list.pop(), None);|---- first mutable borrow occurs here...68| list.push(1);|^^^^||| second mutable borrow occurs here| first borrow later used hereerror[E0499]: cannot borrow `list` as mutable more than once at a time--> src/fifth.rs:69:9|65|assert_eq!(list.pop(), None);|---- first mutable borrow occurs here...69| list.push(2);|^^^^||| second mutable borrow occurs here| first borrow later used hereerror[E0499]: cannot borrow `list` as mutable more than once at a time--> src/fifth.rs:70:9|65|assert_eq!(list.pop(), None);|---- first mutable borrow occurs here...70| list.push(3);|^^^^||| second mutable borrow occurs here| first borrow later used here....** WAY MORE LINES OF ERRORS **....error: aborting due to 11 previous errors
🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀🙀
我的天啊。
编译器对我们所有人的呕吐没有错。我们刚犯了一个主要的Rust罪过:我们在自己的内部存储了对自己的引用。 不知何故,我们设法说服了Rust,这在我们的push和pop实施中是完全合理的(我们确实为之震惊)。 我相信原因是Rust不能仅仅通过 push 和 pop 来告诉引用是对我们自己的引用,或者更确切地说,Rust根本没有这个概念。 引用失败的行为只是一种新出现的行为。