Я немного изменил код Масклинна, чтобы в одном и том же стеке можно было вызывать несколько .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
, поскольку self
является изменяемой ссылкой, а не владельцем. Итак, что компилятор пытается сделать, это перезаписать self.data
, который «заражает» его временем жизни &mut self
. Это тупик: мы хотим, чтобы ссылка действовала для 'a
, но на самом деле она действительна только для жизни &mut self
, и эти времена жизни, как правило, не связаны (и они не должны быть связаны), что оставляет компилятор в замешательстве.
Чтобы помочь компилятору, мы используем std::mem::replace
, чтобы явно переместить срез из self.data
и временно заменить его пустым срезом, , который может быть любым временем жизни . Теперь мы можем сделать что-нибудь с data
, не связывая его с временем жизни &mut self
.