在调用堆栈的末尾,Pin只是一个仅供unsafe代码依赖的语义保证吗?是否有任何完全安全的1Rust构造或API在没有固定的值的情况下无法工作/编译?(除了调用显然需要Pin<T>的函数外)

我想我明白Pin是如何工作的(它并不是真的能做任何事),以及它代表了什么.我只是在想,除了最终在调用堆栈中的unsafe个代码中拥有一个稳定的原始指针之外,这个新类型提供的保证是否对任何事情都有用.

1"安全"是指"不是unsafe",我并不是说标准库中的unsafe代码实际上是不安全/不可靠的

推荐答案

下面的代码是完全安全的(如果你不看BoxString),它包含了一个自引用:

use std::{future::Future, task::Context};
use futures::{future::pending, task::noop_waker_ref};

fn main() {
    let mut future = Box::pin(async {
        let x = String::from("hello");
        let y = x.as_str();
        pending::<()>().await;
        println!("{y}");
    });
    
    // Poll `future` once so it is suspended at the await point
    let mut dummy_ctx = Context::from_waker(noop_waker_ref());
    let _ = future.as_mut().poll(&mut dummy_ctx);
    
    // `future` now contains a self-reference
}

包含borrow 其他变量的变量的async个块生成包含自引用的Future,并且它们不是使用unsafe代码定义的.

当然,这可能是一个不令人满意的例子,因为虽然使用率不是unsafe%,但显式使用现有Pin的代码也不安全--对固定future 的内容的访问隐藏在编译器对代码的转换中.


原则上,您可以定义使用自己address类型,例如将其用作某些表中的键.在这种情况下,Pin保证将确保地址在值Drop之前不会更改,这正是您想要的,以便在删除该值之前拥有稳定的密钥.

这本质上是一种奇怪的自引用情况,因为它使用指向自身的指针,而不是实际上的dereferencing个指针,所以它可以在完全安全的代码中完成.

但是,这并不是使用Pin的实际理由.您通常可以通过使用AtomicU64全局计数器方便地获取唯一ID(不需要释放,因为在您的程序停止运行之前溢出是不可行的),这可以完全在您的类型内部完成,而不需要所有者遵循Pin规则.

为了从使用地址作为ID的Pin规则中受益,您需要做一些更具体的事情,比如希望structstructurally pinned个成员能够获得始终以升序与字段顺序匹配的ID.

在这种情况下,Pin提供了与组合期货相同的好处:每个元素都获得了稳定地址的保证,而不必创建自己的堆分配.(没有作文,你不妨通过堆分配你的内容来创建一个稳定的地址--这就是ouroboros为了实现self 引用without Pin所做的事情.)

Rust相关问答推荐

无需通过ASIO输入音频,并使用cpal进行反馈示例

borrow 和内部IntoIterator

如何访问Rust存储值的内存地址

无法将记录器向下转换回原始 struct

如何为rust trait边界指定多种可能性

在Rust中是否可以使用Rc自动化约束传播

从未排序的链表中删除重复项的铁 rust 代码在ELSE分支的低级上做了什么?

零拷贝按步骤引用一段字节

为什么&;mut buf[0..buf.len()]会触发一个可变/不可变的borrow 错误?

如何重命名 clap_derive 中的子命令占位符?

如何在 Rust 中打印 let-else 语句中的错误?

如何正确使用git2::Remote::push?

如何从borrow 的异步代码运行阻塞代码?

Rust中的一生语法有什么作用?

在 Rust 中,我如何处理请求 javascript 的页面?

判断 is_ok 后重用结果

为什么基于 clap::Parser 读取的大量数字进行计算比硬编码该数字时慢?

TcpStream::connect - 匹配武器具有不兼容的类型

为什么当borrow 变量发生变化时,borrow 变量不会改变?

为什么可以从不可变 struct 的字段中移动?