我需要一些帮助来理解如何指定生命周期,以使Rust理解我正在try 做什么(或者如果不可能,为什么不).

首先,这是我的基本情况,它工作得很好.设想一个工厂类型Foo,它创建Bar,每个Barborrow 创建它的Foo.borrow 的目的是,当Bar被放下时,它可以自动操作创建它的Foo.Foo显然必须比Bar活得更久,这正是我的意图,也是我希望编译器验证的东西.

struct Foo {
    s: String,
    i: u64,
}

struct Bar<'a> {
    parent: Option<&'a mut Foo>,
    s: String,
    i: u64,
}

impl Foo {
    fn new(s: String) -> Foo {
        Foo { s, i: 0 }
    }

    fn push<'a>(&'a mut self, s: String) -> Bar<'a> {
        Bar { parent: Some(self), s, i: 0 }
    }

    fn print(&self) {
        println!("{}:{}", self.s, self.i)
    }
}

impl<'a> Bar<'a> {
    fn print(&self) {
        println!("{}:{}", self.s, self.i)
    }
}

impl<'a> Drop for Bar<'a> {
    fn drop(&mut self) {
        if let Some(parent) = &mut self.parent {
            parent.i += 1;
        }
    }
}

fn main() {
    let mut x = Foo::new("x".to_string());
    {
        let y = x.push("y".to_string());
        y.print(); // y:0
    } // when y is dropped it increments x.i
    x.print(); // x:1
}

现在我希望做的是让这个模式递归,这样y就可以递归地推送一个新的z(其中y必须像预期的那样比z活得更久),依此类推.换句话说,我不希望有单独的FooBar,而是希望有一个类型Foo,它的push方法创建了引用它的新Foo.以下是我经过数小时的修修补补后得出的最好的建议:

struct Foo<'a, 'b> where 'b: 'a {
    parent: Option<&'a mut Foo<'b, 'b>>, // note 1
    s: String,
    i: u64,
}

impl<'a, 'b> Foo<'a, 'b> {
    fn new(s: String) -> Foo<'a, 'b> { // note 2
        Foo { parent: None, s, i: 0 }
    }

    fn push(&'b mut self, s: String) -> Foo<'a, 'b> { // note 2
        Foo { parent: Some(self), s, i: 0 }
    }

    fn print(&self) {
        println!("{}:{}", self.s, self.i);
    }
}

impl<'a, 'b> Drop for Foo<'a, 'b> {
    fn drop(&mut self) {
        if let Some(parent) = &mut self.parent {
            parent.i += 1;
        }
    }
}

fn main() {
    let mut x = Foo::new("x".to_string());
    {
        let y = x.push("y".to_string()); // error 1
        y.print(); // y:0
    } // when y is dropped it increments x.i
    x.print(); // x:1, error 2
}

注意事项和错误详细信息:

  • Note 1:在Foo<'b, 'b>年中使用相同的生命周期两次是错误的,因为我并不是说Foo的两个生命周期都需要匹配.然而,将'a用于任何一个都似乎是错误的,如果不向Foo添加另一个生命周期 参数,我就无法指定一个新的/不同的生命周期 ,这反过来又迫使我在这里指定第三个生命周期 ,这导致了同样的问题.想想看,它是递归的.只是为了好玩,我判断了Rust的C++可变模板的类似功能,但即使这是可能的,它仍然感觉像是一个粗略的解决方案.
  • Note 2这些行闻起来不对劲,因为我在返回类型中使用了生存期,而这些生存期没有出现在函数签名的其他地方,但我不确定该怎么办.
  • Error 1错误是"借来的价值生命周期 不够长"和"x掉在这里,还借着"("here"是最后一行).我的期望是yx的引用只有y那么长,但Rust显然不认为生存期是这样工作的,所以我似乎没有很好地告诉编译器应该如何约束(或不约束)生存期.
  • Error 2这里的错误是"不能borrow x作为不可变的,因为它也被borrow 为可变的",这只是同一问题的另一个症状.我预计,当我们调用x.print()时,位于y内的x的可变借入会消失,但是编译器没有.

当我寻找这个问题的解决方案时,我发现的很多东西都是关于创建循环引用的,其中父级有一个子级列表,每个子级都引用父级.我理解为什么这是困难/不可能的,但我不认为这是我在这里试图做的事情.我的父/工厂类型不维护对其子/小部件的引用;这些引用只有一个方向,从子/小部件到父/工厂.

我也发现自己阅读了铁 rust 规范中与此相关的部分,但老实说,我完全搞不懂.至少有一个关于生命如何实际工作的时刻我还没有经历过,这让我很难理解所有规范级别的总体细节,更不用说与我的问题相关的细节了.

我觉得我想让Rust做它擅长做的事情--验证父母的事情比子元素的事情更长久--但我不确定如何解释我试图对Rust做的事情,因为父母和子元素的事情具有相同的类型.有人能告诉我怎么做或者为什么不能做吗?

推荐答案

对于后代,我采用了@SolomonUcko在 comments 中提出的解决方案--使用具有内部可变性的不可变父引用.以下是我问题中简化后的示例的解析版本:

use std::cell::Cell;

struct Foo<'a> {
    parent: Option<&'a Foo<'a>>,
    s: String,
    i: Cell<u64>, // field manipulated by child uses interior mutability
}

impl<'a> Foo<'a> {
    fn new(s: String) -> Self {
        Foo { parent: None, s, i: Cell::new(0) }
    }

    fn push(&'a self, s: String) -> Self { // non-mut self
        Foo { parent: Some(self), s, i: Cell::new(0) }
    }

    fn print(&self) {
        println!("{}:{}", self.s, self.i.get());
    }
}

impl<'a> Drop for Foo<'a> {
    fn drop(&mut self) {
        if let Some(parent) = &mut self.parent {
            parent.i.replace(parent.i.get() + 1); // mutate parent's Cell contents
        }
    }
}

fn main() {
    let x = Foo::new("x".to_string());
    {
        let y = x.push("y".to_string());
        {
            let z = y.push("z".to_string());
            z.print(); // z:0
        } // z dropped, increments y.i
        y.print(); // y:1
    } // y dropped, increments x.i
    x.print(); // x:1
}

我使用此模式的实际代码还有一个第三方类型的字段,它只是一个Arc左右的包装器(即引用计数),从子对象处理它也很好用,@PeterHall在另一条 comments 中建议这样做.

Rust相关问答推荐

为什么类型需要在这个代码中手动指定,在rust?

是否有可能同时避免不兼容的不透明类型和代码重复?

有没有办法模仿对象安全克隆?

在特征中使用Async时,如何解决不透明类型`impl Future<;out=self>;`不满足其关联的类型边界和警告?

告诉Rust编译器返回值不包含构造函数中提供的引用

如何从ruust中的fig.toml中读取?

如何go 除多余的(0..)在迭代中,当它不被使用时?

为什么我必须使用 PhantomData?在这种情况下它在做什么?

Rust:为什么 &str 不使用 Into

write_buffer 不写入缓冲区而是输出零 WGPU

Rust 编译器不统一在 if let 表达式的分支中都 impl Future 的类型

如何递归传递闭包作为参数?

&self 参数在 trait 的功能中是必需的吗?

没有通用参数的通用返回

是否有适当的方法在参考 1D 中转换 2D 数组

使用 `.` 将 T 转换为 &mut T?

Abortable:悬而未决的期货?

你能用 Rust 和 winapi 制作 Windows 桌面应用程序吗?

传递 Option<&mut T> 时何时需要 mut

在特征中返回一个 Self 类型的值