具有对同一类型的另一个实例的可变引用的结构

问题描述 投票:0回答:2

如何创建一个具有对其自身类型的可选可变引用的结构。不是自我引用,所以实例不同,例如

struct Environment<'a> {
    outer: Option<&'a mut Environment<'a>>,
    // Can be swapped for any other data
    variables: HashMap<Identifier, Expression>, 
}

我也尝试过:

struct Environment<'a, 'b> {
    outer: Option<&'a mut Environment<'b, 'b>>, //...

但没有成功。

我希望能够:

  • 将其保留为参考(无 Box),因为实例将位于堆栈上
  • 保持它,而不会造成额外的运行时性能影响(Rc/RefCell)
  • 在任意外部环境中改变
    variables
  • 无法更改
    outer.outer
    背后的值(据我所知这可能会导致不健全)
  • 保持一个内部环境,就像它可变地(排他地)借用外部环境一样,只要它存在(所以没有来自同一个外部/父级的两个内部/子级)

这是一种分割借用。

variables
链上的
outer
是借用的
&mut
,但
outer
&
,而
Environment
作为一个整体也是
&mut
'd

我知道问题在于 &mut T 对 T 是不变的。有什么方法可以使

&mut T
变得协变,就像它是
&T
一样吗?

编辑: 所需的示例用法(无法编译)

struct Environment<'a> {
    outer: Option<&'a mut Environment<'a>>,
    value: i32,
}

impl<'a> Environment<'a> {
    fn new(value: i32) -> Self {
        Self { outer: None, value }
    }
    
    fn child<'b>(&'b mut self, value: i32) -> Environment<'b>
    where
        // This is the relationship I would want
        // parent environment outlives child environment
        'a: 'b
    {
        Self { outer: Some(self), value }
    }
}

fn user_that_creates_child(env: &mut Environment) {
    let child_env = env.child(11);
    // Do something with the child
    child_env.outer.unwrap().value = 8; // This should be possible
    // child_env.outer.unwrap().outer = // This shouldn't be
}

fn main() {
    let mut root_env = Environment::new(7);
}

编译器建议与我想要的关系相反,即子函数的

where 'b: 'a

rust borrow-checker
2个回答
0
投票

这是可行的解决方案:

struct Environment<'a> {
    outer: Option<&'a mut Environment<'a>>,
    value: i32,
}

impl<'a> Environment<'a> {
    fn new(value: i32) -> Self {
        Self { outer: None, value }
    }

    // Lifetime 'a is enough here.
    fn child(&'a mut self, value: i32) -> Environment<'a> {
        Self {
            outer: Some(self),
            value,
        }
    }
}

// In the original example, the compiler coerced
// &mut Environment into &'a mut Environment<'b>, that
// doesn't feet the `child` method requirements.
fn user_that_creates_child<'a>(env: &'a mut Environment<'a>) {
    let child_env = env.child(11);
    child_env.outer.unwrap().value = 8;
}

fn main() {
    let mut root_env = Environment::new(7);
    user_that_creates_child(&mut root_env); // Compiles!
}

0
投票

编写的代码的问题是,

outer
指针定义了子结构的生命周期。

因此每个孩子都必须与外部环境生活在一起。为子环境和外部环境定义多重生命周期没有任何意义,并且会导致不同的编译器错误。

由于您不希望能够更改外部值,但可以复制可复制的值,因此最好的解决方案应该是使用

Cell
,它提供了零抽象解决方案。

use std::cell::Cell;

struct Environment<'a> {
    outer: Option<&'a Environment<'a>>,
    value: Cell<i32>,
}

impl<'a> Environment<'a> {
    fn new(value: i32) -> Self {
        Self { outer: None, value: Cell::new(value) }
    }

    fn child(&'a self, value: i32) -> Environment<'a>
    {
        Self { outer: Some(self), value: Cell::new(value) }
    }
}


fn user_that_creates_child(env: &mut Environment) {
    let child_env = env.child(11);
    // Do something with the child
    child_env.outer.unwrap().value.set(8); // This should be possible
    // child_env.outer.unwrap().outer = // This shouldn't be
}

fn main() {
    let mut root_env = Environment::new(7);
    user_that_creates_child(&mut root_env);
}
© www.soinside.com 2019 - 2024. All rights reserved.