【Rust日报】2023-07-18 Pin- 温故而知新

有些事情你总是学了又忘记(或者说你从来就没学过?)

对我来说,其中之一就是在Rust中 Pin/Unpin 。

每次我读到有关固定的解释,我的大脑就像 👍 ,几周后就像 🤔 🤨 。

所以,我写这篇文章是为了强迫我的大脑记住这些知识。我们看看效果如何!

Pin

Pin 是一种指针,可以看作是 &mut T 和 &T 之间的折中。

Pin<&mut T> 的重点是说:

  1. 这个值可以被修改(就像 &mut T 一样),但是

  2. 这个值不能被移动(不像 &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?

怎么回事?就像一位聪明的编译器曾经说过的

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值