Macro std::pin::pin

1.68.0 · source ·
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

使用 Generators

#![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(()) => {},
    }
}
Run

Remarks

正是因为一个值被固定到本地存储,最终的 Pin<&mut T> 引用最终借用了一个绑定到该块的本地:它无法逃脱它。

例如,以下内容无法编译:

use core::pin::{pin, Pin};

let x: Pin<&mut Foo> = {
    let x: Pin<&mut Foo> = pin!(Foo { /* … */ });
    x
}; // <- Foo 已被丢弃
stuff(x); // 错误:使用了已经丢弃的值
Run
Error 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 构造函数。