我稍微修改了 Masklinn 的代码以允许多个.pop()
在同一个堆栈上调用 s:
struct StackLike<'a, X> {
data: &'a mut [X],
}
impl<'a, X> StackLike<'a, X> {
pub fn pop(&mut self) -> Option<&'a mut X> {
let data = std::mem::replace(&mut self.data, &mut []);
if let Some((last, subslice)) = data.split_last_mut() {
self.data = subslice;
Some(last)
} else {
None
}
}
}
fn main() {
let mut data = [1, 2, 3, 4, 5];
let mut stack = StackLike { data: &mut data };
let x = stack.pop().unwrap();
let y = stack.pop().unwrap();
println!("X: {}, Y: {}", x, y);
}
这里棘手的部分是这一行(为了明确起见,我添加了类型注释):
let data: &'a mut [X] = std::mem::replace(&mut self.data, &mut []);
我们替换self.data
暂时使用一个空切片,以便我们可以分割切片。如果你简单地写
let data: &'a mut [X] = self.data;
编译器会不高兴:
error[E0312]: lifetime of reference outlives lifetime of borrowed content...
--> src/main.rs:7:33
|
7 | let data: &'a mut [X] = self.data;
| ^^^^^^^^^
|
note: ...the reference is valid for the lifetime `'a` as defined on the impl at 5:6...
--> src/main.rs:5:6
|
5 | impl<'a, X> StackLike<'a, X> {
| ^^
note: ...but the borrowed content is only valid for the anonymous lifetime #1 defined on the method body at 6:5
--> src/main.rs:6:5
|
6 | / pub fn pop(&mut self) -> Option<&'a mut X> {
7 | | let data: &'a mut [X] = self.data;
8 | | if let Some((last, subslice)) = data.split_last_mut() {
9 | | self.data = subslice;
... |
13 | | }
14 | | }
| |_____^
据我了解,问题在于self.data
是可变引用,而可变引用不是Copy
(请记住,您一次只能拥有一个)。而且你无法搬出self.data
since self
是可变引用,而不是所有者。所以编译器试图做的就是重新借用self.data
,它的生命周期“感染”它&mut self
。这是一个死胡同:我们希望引用能够存在'a
,但它实际上只在生命周期内有效&mut self
,并且这些生命周期通常是不相关的(并且它们不需要相关),这让编译器感到困惑。
为了帮助编译器,我们使用std::mem::replace
显式地将切片移出self.data
并暂时用空切片替换它,这可以是任何一生 https://stackoverflow.com/q/56066534/3650362。现在我们可以做任何事情data
不与生命周期纠缠在一起&mut self
.