Peek
有一件事我们上次甚至没有去做,那就是 peek。我们开始吧,我们需要做的就是返回对列表头元素的引用(如果它存在的话)。听起来很简单,让我们试试:
pub fn peek(&self) -> Option<&T> {
self.head.map(|node| {
&node.elem
})
}
> cargo build
error[E0515]: cannot return reference to local data `node.elem`
--> src/second.rs:37:13
|
37 | &node.elem
| ^^^^^^^^^^ returns a reference to data owned by the current function
error[E0507]: cannot move out of borrowed content
--> src/second.rs:36:9
|
36 | self.head.map(|node| {
| ^^^^^^^^^ cannot move out of borrowed content
叹。 Rust,现在怎么办?
map按值获取self,这会将Option从它所在的对象中移出。以前这是好的,因为我们刚刚把它拿出来,但现在我们真的想把它留在原来的地方。正确的处理方法是使用 Option 上的 as_ref 方法,它有以下定义:
impl<T> Option<T> {
pub fn as_ref(&self) -> Option<&T>;
}
它将 Option 降级为对其内部的引用。我们可以自己用一个显式的匹配来完成,但是不可以。这确实意味着我们需要做一个额外的解引用来切断额外的间接寻址,但谢天谢地,operator 为我们处理。
pub fn peek(&self) -> Option<&T> {
self.head.as_ref().map(|node| {
&node.elem
})
}
cargo build
Finished dev [unoptimized + debuginfo] target(s) in 0.32s
搞定了。
我们也可以使用 as_mut 创建一个可变的版本:
pub fn peek_mut(&mut self) -> Option<&mut T> {
self.head.as_mut().map(|node| {
&mut node.elem
})
}
lists::cargo build
别忘了测试一下:
#[test]
fn peek() {
let mut list = List::new();
assert_eq!(list.peek(), None);
assert_eq!(list.peek_mut(), None);
list.push(1); list.push(2); list.push(3);
assert_eq!(list.peek(), Some(&3));
assert_eq!(list.peek_mut(), Some(&mut 3));
}
cargo test
Running target/debug/lists-5c71138492ad4b4a
running 3 tests
test first::test::basics ... ok
test second::test::basics ... ok
test second::test::peek ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured
这很好,但是我们并没有真正的测试看看我们是否可以改变peek_mut的返回值,是吗?如果一个引用是可变的,但是没有人对它进行变异,那么我们真的测试过它的可变性吗?让我们试着在这个选项上使用map,在以下位置输入一个深刻的值:
#[test]
fn peek() {
let mut list = List::new();
assert_eq!(list.peek(), None);
assert_eq!(list.peek_mut(), None);
list.push(1); list.push(2); list.push(3);
assert_eq!(list.peek(), Some(&3));
assert_eq!(list.peek_mut(), Some(&mut 3));
list.peek_mut().map(|&mut value| {
value = 42
});
assert_eq!(list.peek(), Some(&42));
assert_eq!(list.pop(), Some(42));
}
> cargo test
error[E0384]: cannot assign twice to immutable variable `value`
--> src/second.rs:100:13
|
99 | list.peek_mut().map(|&mut value| {
| -----
| |
| first assignment to `value`
| help: make this binding mutable: `mut value`
100 | value = 42
| ^^^^^^^^^^ cannot assign twice to immutable variable ^~~~~
编译器抱怨值是不可变的,但是我们很清楚地写下了&mut value;给出了什么?事实证明,以这种方式编写闭包的参数并没有指定值是可变引用。相反,它会创建一个模式,该模式将与闭包的参数相匹配;|&mut value |意味着“参数是一个可变的引用,但是请将它指向的值复制到value中”。如果我们只使用 |value |,那么值的类型将是&mut i32,我们实际上可以改变head:
#[test]
fn peek() {
let mut list = List::new();
assert_eq!(list.peek(), None);
assert_eq!(list.peek_mut(), None);
list.push(1); list.push(2); list.push(3);
assert_eq!(list.peek(), Some(&3));
assert_eq!(list.peek_mut(), Some(&mut 3));
list.peek_mut().map(|value| {
*value = 42
});
assert_eq!(list.peek(), Some(&42));
assert_eq!(list.pop(), Some(42));
}
cargo test
Running target/debug/lists-5c71138492ad4b4a
running 3 tests
test first::test::basics ... ok
test second::test::basics ... ok
test second::test::peek ... ok
test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured
好多了!
Last updated
Was this helpful?