< 返回版块

Matheritasiv 发表于 2023-12-14 22:01

Tags:生命周期

use std::cell::RefCell;
use std::ops::Add;

struct MyData<'a> {
    data: RefCell<&'a mut dyn MyTrait>,
}

trait MyTrait {}
impl<'a> MyTrait for &'a mut i32 {}
impl<'a, 'b> MyTrait for (&'a mut dyn MyTrait, &'b mut dyn MyTrait) {}
impl<'a, 'b> MyTrait for &'b MyData<'a> {}

impl<'a> Add for MyData<'a> {
    type Output = Self;
    fn add(self, other: Self) -> Self::Output {
        MyData {
            data: RefCell::new(Box::leak(Box::new(
                (self.data.into_inner(), other.data.into_inner())
            ))),
        }
    }
}

impl<'a, 'b> Add for &'b MyData<'a> {
    type Output = MyData<'b>;
    fn add(self, other: Self) -> Self::Output {
        MyData {
            data: RefCell::new(Box::leak(Box::new(
                (Box::leak(Box::new(self)) as &mut dyn MyTrait,
                 Box::leak(Box::new(other)) as &mut dyn MyTrait)
            ))),
        }
    }
}

impl<'a, 'b> Add<&'b MyData<'a>> for MyData<'a> {
    type Output = MyData<'b>;
    fn add(self, other: &'b MyData<'a>) -> Self::Output {
        MyData {
            data: RefCell::new(Box::leak(Box::new(
                (self.data.into_inner(),
                 Box::leak(Box::new(other)) as &mut dyn MyTrait)
            ))),
        }
    }
}

fn main() {
    let mut n = 1;
    let mut ref_n = &mut n;
    let x = MyData { data: RefCell::new(&mut ref_n) };
    let y = &x + &x;
    //let _ = y + &x;
    drop(x);
}

这段代码重载了三个加法运算:

  • fn add(_: MyData<'a>, _: MyData<'a>) -> MyData<'a>
  • fn add(_: &'b MyData<'a>, _: &'b MyData<'a>) -> MyData<'b>
  • fn add(_: MyData<'a>, _: &'b MyData<'a>) -> MyData<'b>

main函数里面的let _ = y + &x;注释掉可以通过检查,但加上这句会报错,想知道问题出在哪以及如何修改。

playground链接附在后面。


Ext Link: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=497b781639a77c058ec2075fa4568b0f

评论区

写评论
作者 Matheritasiv 2023-12-16 23:28
struct Digits<'a, 'b>(&'a Bump, &'a RefCell<DigitsDatum<'a>>, PhantomData<&'b &'a ()>);

make_trait_add!['a, 'b: 'a, 'c;     Digits<'a, 'c>,     Digits<'b, 'c>; Digits<'a, 'c>];
make_trait_add!['a, 'b: 'a, 'c;     Digits<'a, 'c>, &'c Digits<'b, 'c>; Digits<'a, 'c>];
make_trait_add!['a, 'b: 'a, 'c; &'c Digits<'a, 'c>,     Digits<'b, 'c>; Digits<'a, 'c>];
make_trait_add!['a, 'b: 'a, 'c; &'c Digits<'a, 'c>, &'c Digits<'b, 'c>; Digits<'a, 'c>];

加了一个PhantomData来表示每个Digits对象的生命周期,并且不打算让'a发生改变了,所有RefCell的引用的生命周期都与内存池相同。这样Add trait可以像下面那样重载,因为'c是covariance的。

苦瓜小仔 2023-12-16 11:01

&'a RefCell<DigitsDatum<'a> 也是一个永久借用,所以你可能要引入一个生命周期参数 &'b RefCell<DigitsDatum<'a>

但h的标注用在Add trait上会报"unconstrained lifetime parameter"错误,请问这种情况该怎么表达约束?

h 的生命周期标注无法应用到 Add trait 上,搜索了一下,是因为 'c 只出现在了关联类型上,从而它需要变成 GAT,即 type Output<'c> where 'a: 'c, 'b: 'c;。解决方法是

  • 定义成 inherent method,也就是把 h 写到 impl MyData 里面
  • 或者定义你自己的,带 GAT 的 trait
作者 Matheritasiv 2023-12-16 05:04

playground

按照我对invariance的理解,这里对f,g,h的标注应该更符合我本意,但h的标注用在Add trait上会报"unconstrained lifetime parameter"错误,请问这种情况该怎么表达约束?

--
👇
苦瓜小仔: 文章在这:&'a Type<'a> 变成了永远借用:与生命周期标注交互

我简化了代码(但函数签名没变),如果你只想知道原帖的代码思路,见注释: playground (简化了一些无关核心问题的代码)

改成 MyData<'b> 能通过的原因,见注释:playground

作者 Matheritasiv 2023-12-16 00:00

解释一下做出这种设计的动机。我想实现一个对实数作精确运算的类,采用了如下这种带记忆的迭代器来表达一个惰性求值的无限不循环小数:

struct Digits<'a>(&'a Bump, &'a RefCell<DigitsDatum<'a>>);
struct DigitsDatum<'a> {
    stream: &'a mut dyn Iterator<Item=i32>,
    digits: BVec<'a, i32>,
}

这里的Bump是arena内存池,BVec是分配在其中的Vec。在本提问中这不是一个很重要的点,所以我用Box::leak()来代替了其上的内存分配。由于一个固定的实数对外界来说是不变的,但对其每一个数位的求值需要改变迭代器,所以引入了内部可变性。如果这一块有更好的设计也欢迎留言讨论。

作者 Matheritasiv 2023-12-15 23:33

感谢解答👍

--
👇
苦瓜小仔: 文章在这:&'a Type<'a> 变成了永远借用:与生命周期标注交互

我简化了代码(但函数签名没变),如果你只想知道原帖的代码思路,见注释: playground (简化了一些无关核心问题的代码)

改成 MyData<'b> 能通过的原因,见注释:playground

苦瓜小仔 2023-12-15 21:54

文章在这:&'a Type<'a> 变成了永远借用:与生命周期标注交互

我简化了代码(但函数签名没变),如果你只想知道原帖的代码思路,见注释: playground (简化了一些无关核心问题的代码)

改成 MyData<'b> 能通过的原因,见注释:playground

苦瓜小仔 2023-12-15 14:26

我能够解释,但可能内容会很长,打算用一篇文章来描述 :)

作者 Matheritasiv 2023-12-15 11:04

--
👇
yuyidegit:

impl<'a, 'b> Add<&'b MyData<'a>> for MyData<'a> {
    type Output = MyData<'b>;
    fn add(self, other: &'b MyData<'a>) -> Self::Output {
        MyData {
            data: RefCell::new(Box::leak(Box::new(
                (self.data.into_inner(),
                 Box::leak(Box::new(other)) as &mut dyn MyTrait)
            ))),
        }
    }
}

改成

impl<'a, 'b> Add<&'b MyData<'a>> for MyData<'b> {
    type Output = MyData<'b>;
    fn add(self, other: &'b MyData<'a>) -> Self::Output {
        MyData {
            data: RefCell::new(Box::leak(Box::new(
                (self.data.into_inner(),
                 Box::leak(Box::new(other)) as &mut dyn MyTrait)
            ))),
        }
    }
}

十分感谢!但还是想知道为什么前一种写法不行,有没有什么调试工具可以看到编译器内部处理这些生命周期约束的过程?

yuyidegit 2023-12-15 08:08
impl<'a, 'b> Add<&'b MyData<'a>> for MyData<'a> {
    type Output = MyData<'b>;
    fn add(self, other: &'b MyData<'a>) -> Self::Output {
        MyData {
            data: RefCell::new(Box::leak(Box::new(
                (self.data.into_inner(),
                 Box::leak(Box::new(other)) as &mut dyn MyTrait)
            ))),
        }
    }
}

改成

impl<'a, 'b> Add<&'b MyData<'a>> for MyData<'b> {
    type Output = MyData<'b>;
    fn add(self, other: &'b MyData<'a>) -> Self::Output {
        MyData {
            data: RefCell::new(Box::leak(Box::new(
                (self.data.into_inner(),
                 Box::leak(Box::new(other)) as &mut dyn MyTrait)
            ))),
        }
    }
}
1 共 9 条评论, 1 页