下面的代码是完全安全的(如果你不看Box
和String
),它包含了一个自引用:
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
规则中受益,您需要做一些更具体的事情,比如希望struct
的structurally pinned个成员能够获得始终以升序与字段顺序匹配的ID.
在这种情况下,Pin
提供了与组合期货相同的好处:每个元素都获得了稳定地址的保证,而不必创建自己的堆分配.(没有作文,你不妨通过堆分配你的内容来创建一个稳定的地址--这就是ouroboros
为了实现self 引用without Pin
所做的事情.)