Avatar

Strick

Rust 异步笔记

2024/09/02Rust


Pin 的存在是为了解决自引用问题

Future

trait Future {
    type Output;
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>;
}

Stream 又名 AsyncIterator,也就是一个可以被重复拉取的 Future, 以 Option 是否为 None 判定该 Stream 是否结束。

trait Stream {
    type Item;
    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>>;
}

将字段包装为 Pin 类型

假设有这样的结构体

struct Foo {
    bar: Bar,
    baz: Baz,
}

在为一个 Foo 实现 FutureStream 时, 虽然接收器是 self: Pin<&mut Self>,但是我们只能拿到 bar 字段的可变引用 &mut Bar, 如果需要调用它的 poll 方法,就需要将这个字段包装为 Pin<&mut Bar> 类型。

使用 pin-project / pin-project-lite 投影

pin-project 采用过程宏实现。

use pin_project::pin_project;
 
#[pin_project]
struct Foo {
    #[pin]
    bar: Bar,
    // 这个字段不会被自动包装为 `Pin`
    baz: Baz,
}

pin-project-lite 轻量级版本,采用声明宏实现。

use pin_project_lite::pin_project;
 
pin_project! {
    struct Foo {
        #[pin]
        bar: Bar,
        // 这个字段不会被自动包装为 `Pin`
        baz: Baz,
    }
}

这两个宏都可以自动为结构体生成 project 方法, 该方法返回一个匿名枚举类型,其中包含了所有字段的引用, 其中 #[pin] 属性会自动该字段包装为 Pin 类型。

let this = self.project();
let bar: Pin<&mut Bar> = this.bar;
let baz: &mut Baz = this.baz;

注意:以上的 project 表示将 Pin<&mut Self> 投影为 (Pin<&mut Bar>, &mut Baz)。 它的含义是「投影」,而不是「项目」。 因此正确读音是 /prəˈdʒɛkt/ 而不是 /ˈprɒdʒɛkt/。