rust “Pin”是否具有不涉及不安全代码的目的?

wfauudbj  于 12个月前  发布在  其他
关注(0)|答案(2)|浏览(139)

Pin是否只是一个语义保证,仅由unsafe代码依赖,在调用堆栈的末尾?是否有任何完全安全的Rust结构或API在没有固定值的情况下无法工作/编译?(除了调用需要Pin<T>的函数)
我想我理解Pin是如何“工作”的(它并没有真正做任何事情),以及它代表了什么。我只是想知道,除了在调用堆栈的unsafe代码中的某个地方最终拥有一个稳定的原始指针之外,这种新类型提供的保证是否有用。
1“safe”,即“not unsafe“,我并不是在暗示标准库中的unsafe代码实际上是不安全/不可靠的

w8biq8rn

w8biq8rn1#

下面的代码是完全安全的(如果你不看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
}

字符串
async块包含借用其他变量的变量,产生包含自引用的Future s,并且它们不是使用unsafe代码定义的。
当然,这可能是一个不令人满意的例子,因为虽然用法不是unsafe,但显式使用已经存在的Pin也是不安全的代码-对固定未来内容的访问隐藏在编译器对代码的转换中。
原则上,你可以定义一个类型,它使用自己的 address,比如在某个表中使用它作为键。在这种情况下,Pin保证将确保地址永远不会改变,直到值被Drop ped,这正是你想要的,以便在删除值之前有一个稳定的键。
这本质上是自引用的一种奇怪情况,因为它使用指向自身的指针,但不是通过实际 * 解引用 * 这些指针,所以它可以在完全安全的代码中完成。
但是,这并不是使用Pin的实际原因。您通常可以通过使用AtomicU64全局计数器方便地获得唯一ID(不需要释放,因为在程序停止运行之前不可能溢出),这可以完全在类型内部完成,而不需要所有者遵循Pin规则。
为了从Pin使用地址作为ID的规则中获益,您需要做一些更具体的事情,例如希望struct的结构固定成员能够获得总是以升序匹配字段顺序的ID。
在这种情况下,Pin提供了与组合future相同的好处:每个元素都得到了稳定地址的保证,而不必创建自己的堆分配。(没有组合,你还不如通过堆分配你的内容来创建一个稳定地址--这就是ouroboros在没有Pin的情况下启用自引用的方法。)

gzszwxb4

gzszwxb42#

the standard librarythis Cloudflare blog post都解释了Pin是为自引用结构体而存在的(尽管它们没有排除它用于其他目的的实用性,但它们没有提到任何用途)。
从技术上讲,自引用结构体不能用完全安全的代码编写,因此需要使用unsafe。然而,Pin的存在是因为没有它,就没有办法在安全代码中安全地使用自引用结构体,因为编译器可以并且确实在某些情况下移动值。因此,目标是将潜在的不安全行为限制在unsafe块上,并且不让编译器通过移动值来代表自己引入它,这将被认为是编译器中的bug。

相关问题