pub macro pin($value:expr $(,)?) { ... }
Expand description
通过在本地固定 value: T
来构建 Pin<&mut T>
。
与 Box::pin
不同,这不会创建新的堆分配。如下所述,该元素可能仍会在堆上结束。
宏执行的本地固定通常称为 栈
固定。
在 async
上下文之外,局部变量确实存储在栈中。然而,在 async
函数或块中,任何穿过 .await
点的局部变量都是 Future
捕获的状态的一部分,并将使用它们的存储。
该存储可以在堆上或栈上。
因此,本地固定是一个更准确的术语。
如果给定值的类型未实现 Unpin
,则此宏以防止移动的方式将值固定在内存中。
另一方面,如果该类型确实实现了 Unpin
,则 Pin<&mut T>
的行为类似于 &mut T
,并且 mem::replace()
或 mem::take()
等操作将允许移动值。
有关详细信息,请参见 the Unpin
section of the pin
module。
Examples
基本用法
use core::pin::{pin, Pin};
fn stuff(foo: Pin<&mut Foo>) {
// …
}
let pinned_foo = pin!(Foo { /* … */ });
stuff(pinned_foo);
// 或者,直接:
stuff(pin!(Foo { /* … */ }));
Run手动轮询 Future
(没有 Unpin
边界)
use std::{
future::Future,
pin::pin,
task::{Context, Poll},
thread,
};
/// 运行一个 future 直到完成。
fn block_on<Fut: Future>(fut: Fut) -> Fut::Output {
let waker_that_unparks_thread = // …
let mut cx = Context::from_waker(&waker_that_unparks_thread);
// 固定 future,以便可以对其进行轮询。
let mut pinned_fut = pin!(fut);
loop {
match pinned_fut.as_mut().poll(&mut cx) {
Poll::Pending => thread::park(),
Poll::Ready(res) => return res,
}
}
}
Run使用 Generator
s
#![feature(generators, generator_trait)]
use core::{
ops::{Generator, GeneratorState},
pin::pin,
};
fn generator_fn() -> impl Generator<Yield = usize, Return = ()> /* not Unpin */ {
// 允许生成器是自引用的 (不是 `Unpin`) vvvvvv 以便本地人可以跨越屈服点。
static || {
let foo = String::from("foo");
let foo_ref = &foo; // ------+
yield 0; // | <- crosses yield point!
println!("{foo_ref}"); // <--+
yield foo.len();
}
}
fn main() {
let mut generator = pin!(generator_fn());
match generator.as_mut().resume(()) {
GeneratorState::Yielded(0) => {},
_ => unreachable!(),
}
match generator.as_mut().resume(()) {
GeneratorState::Yielded(3) => {},
_ => unreachable!(),
}
match generator.resume(()) {
GeneratorState::Yielded(_) => unreachable!(),
GeneratorState::Complete(()) => {},
}
}
RunRemarks
正是因为一个值被固定到本地存储,最终的 Pin<&mut T>
引用最终借用了一个绑定到该块的本地:它无法逃脱它。
例如,以下内容无法编译:
ⓘ
use core::pin::{pin, Pin};
let x: Pin<&mut Foo> = {
let x: Pin<&mut Foo> = pin!(Foo { /* … */ });
x
}; // <- Foo 已被丢弃
stuff(x); // 错误:使用了已经丢弃的值
RunError message
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:9:28
|
8 | let x: Pin<&mut Foo> = {
| - borrow later stored here
9 | let x: Pin<&mut Foo> = pin!(Foo { /* … */ });
| ^^^^^^^^^^^^^^^^^^^^^ creates a temporary value which is freed while still in use
10 | x
11 | }; // <- Foo 已被丢弃
| - temporary value is freed at the end of this statement
|
= note: consider using a `let` binding to create a longer lived value
这使得 pin!
在打算返回它们时不适合固定值。
取而代之的是,该值预计将在 unpinned 周围传递,直到它被使用的点,然后使用 pin!
在本地固定该值是有用的,甚至是明智的。
如果您确实需要返回固定值,请考虑改用 Box::pin
。
另一方面,使用 pin!
的本地固定可能比使用 Box::pin
固定到新的堆分配中更便宜。
此外,由于不需要分配器,pin!
是主要的非 unsafe
#![no_std]
兼容的 Pin
构造函数。