< 返回版块

aariety 发表于 2020-12-09 16:51

Tags:pin,自引用,生命周期

通过学习官方文档中std::pin模块的内容我已经知道了如何实现自引用。但是还有下面这种情况。

// 下面的 A, B, get_b() 是来自某个外部库的结构和函数。其具体实现未知,这里只是简单模拟
pub struct A;
pub struct B<'a>(&'a A);
pub fn get_b(a: &A) -> B<'_> {
    B(&a)
}

// 我想实现这样一个类似与自引用的结构体。
pub struct C {
    // 字段 b 由字段 a 通过方法 get_b 产生。 a 与 b 的生命周期都与 C 的实例相同。
    a: A,
    b: B<'_>, // 这里最好能把生命周期参数去掉
    _pin: PhantomPinned,
}

std::pin的自引用示例中,它把引用作为指针存储在结构体里,因此就去掉了生命周期。但在上面这个例子里,生命周期是以类型参数的形式存在,我不知道有没有办法绕过。

评论区

写评论
chansia 2020-12-11 12:28

自引用结构确实不好创建, 可以试试Pin<Box<C>>:

use std::marker::PhantomPinned;
use std::pin::Pin;
use std::ptr::NonNull;

pub struct A;
pub struct B(NonNull<A>);

pub struct C {
    a: A,
    b: B,
    _pin: PhantomPinned,
}

impl C {
    fn new(a: A) -> Pin<Box<Self>> {
        let c = Self {
            a,
            b: B(NonNull::dangling()),
            _pin: PhantomPinned,
        };

        let mut boxed = Box::pin(c);
        unsafe {
            Pin::get_unchecked_mut(Pin::as_mut(&mut boxed)).b = B(NonNull::from(&boxed.a));
        }

        boxed
    }
}
作者 aariety 2020-12-10 07:56

因为我假设 B 是来自外部库的,我不知道其内部实现。我在这里写的结构不过是我为了简化问题模拟的。

--
👇
tcz717: 为什么不再B里用指针呢

pub struct B(NonNull<A>);
tcz717 2020-12-10 05:48

为什么不再B里用指针呢

pub struct B(NonNull<A>);
作者 aariety 2020-12-09 18:55

用指针不行的,我试过了。

假如我用 B<'a> 这个生命周期是不能省掉的。即使我将 B<'a> 放到 NonNull 里这个生命周期也不能去掉,否则编译器会报错的。

假如我保留这个生命周期,那么 C 可能就会变成这样:

struct C<'a> {
    a: NonNull<A>,
    b: B<'a>,
}

这样也不行,因为我甚至没办法用函数创建实例。比如在下面这个函数里:

fn new<'a>() -> C<'a> { ... }

看到这个函数签名了吗。没有参数,但却有一个带'a的返回值,这是行不通的。(这一点可以参考这里 get_str 这个例子)

--
👇
zhuxiujia: 用NonNull+unsafe{} 表示指针了,可以自引用。 指针类型的数据结构很多呀,比如Box,Rc,Arc, 以上这些都可以安全的 ’自引用‘

zhuxiujia 2020-12-09 18:04

用NonNull+unsafe{} 表示指针了,可以自引用。 指针类型的数据结构很多呀,比如Box,Rc,Arc, 以上这些都可以安全的 ’自引用‘

1 共 5 条评论, 1 页