有些事情你总是学了又忘记(或者说你从来就没学过?)
对我来说,其中之一就是在Rust中 Pin/Unpin
。
每次我读到有关固定的解释,我的大脑就像 👍 ,几周后就像 🤔 🤨 。
所以,我写这篇文章是为了强迫我的大脑记住这些知识。我们看看效果如何!
Pin
Pin 是一种指针,可以看作是 &mut T
和 &T
之间的折中。
Pin<&mut T>
的重点是说:
这个值可以被修改(就像
&mut T
一样),但是这个值不能被移动(不像
&mut T
)
为什么?因为有些值必须不能移动,或者需要特别小心地去移动。
一个典型的例子就是自指数据结构。在使用 async
时,它们会自然地出现,因为未来值往往会在引用自己的本地值。
这个看似温和的 Future:
async fn self_ref() {
let mut v = [1, 2, 3];
let x = &mut v[0];
tokio::time::sleep(Duration::from_secs(1)).await;
*x = 42;
}
需要一个自我引用的结构,因为在底层,futures
是状态机(不像闭包)。
请注意, self_ref
在第一个 await
处将控制权传递回调用者。这意味着尽管 v
和 x
看起来像普通的堆栈变量,但在这里可能发生了更复杂的事情。
编译器希望生成类似这样的内容:
enum SelfRefFutureState {
Unresumed, // Created and wasn't polled yet.
Returned,
Poisoned, // `panic!`ed.
SuspensionPoint1, // First `await` point.
}
struct SelfRefFuture {
state: SelfRefFutureState,
v: [i32; 3],
x: &'problem mut i32, // a "reference" to an element of `self.v`,
// which is a big problem if we want to move `self`.
// (and we didn't even consider borrowchecking!)
}
但是!如果你想的话,你可以移动 SelfRefFuture
,这会导致 x
指向无效的内存。
let f = self_ref();
let boxed_f = Box::new(f); // Evil?
let mut f1 = self_ref();
let mut f2 = self_ref();
std::mem::swap(&mut f1, &mut f2); // Blasphemy?
怎么回事?就像一位聪明的编译器曾经说过的