我想定义一个结构,Env它可以选择包含对另一个的(可变)引用Env,称为 it's parent。在所有应用程序中,可以很容易地证明 将会parent比引用更长寿Env。然而,我无法弄清楚如何在类型系统中表达这一点。
我的第一次尝试使我得到了以下结果
struct Env<'a> {
parent: Option<&'a mut Env<'a>>
}
Run Code Online (Sandbox Code Playgroud)
虽然可以编译,但是这两个Env对象现在共享相同的生命周期参数。孩子Env的寿命比孩子短parent,所以这很快就会失败。
我想表达的,大概是这样的:
struct Env<'r> {
parent: Option<&'r mut Env<'e>> where 'e : 'r
}
Run Code Online (Sandbox Code Playgroud)
父级Env有一些(不同的!)生命周期参数'b,该参数比'a确保其安全的参数要长。问题是,如果我们让'e一个参数Env有两个生命周期参数,那么我们又回到了开始的地方。
我正在通过 Crafting Interpreters 进行工作,它使用环境来跟踪解释器中存储的值。对于 Rust 函数的生命周期,我想将给定的环境包装在另一个环境中。函数退出后,新环境将被销毁,并返回原始环境。
我已将其缩减为以下(大部分是最小的)示例。
fn main() {
let mut env = Env {
parent: None,
val: "Global".to_string(),
};
ex_block(3, &mut env);
}
struct Env<'a> {
parent: Option<&'a mut Env<'a>>,
val: String,
}
impl<'a> Env<'a> {
fn walk_up(&mut self) {
println!("{}", self.val);
match &mut self.parent {
Some(env) => { env.walk_up(); },
None => { }
}
}
}
fn ex_block<'r, 'e>(further_levels: u8, env: &'r mut Env<'e>) {
if further_levels > 0 {
let mut child: Env = Env {
/* ERROR: This line somehow implies 'r must outlive 'e which is impossible */
parent: Some(env),
val: format!("Level {}", further_levels),
};
ex_block(further_levels-1, &mut child);
} else {
env.walk_up();
}
}
Run Code Online (Sandbox Code Playgroud)
Env它的父母呢?一个环境结束其生命周期后,原始环境需要仍然存在。这正是借贷的目的。
Env物体都放在一个竞技场里许多讨论树的在线资源建议将节点存储在 Vec 中并使用索引而不是引用。我发现这并不令人满意,因为它只是通过将引用重新实现为索引来回避整个问题。它不如在 C++ 中使用智能指针安全。
这类似于泛型结构,引用相同类型但具有任何具体类型,但是,当我尝试时,我收到了以下有趣的错误:
错误:实例化时达到递归限制
ex_block::<Env<Env<Env<Env<Env<E...>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
制作一个特征并使用dyn是可行的,但它需要在运行时进行函数查找:
struct Env<'a> {
parent: Option<&'a mut dyn EnvTrait>,
}
Run Code Online (Sandbox Code Playgroud)
这里的问题不在于Env持有对其父级的引用。这完全没问题(只要您不创建自我引用值,但那是另一个主题)。
该问题是由于泛型生命周期的可变引用是不变的这一事实引起的(另请参阅此 GitHub 线程)。事实上,如果您仅使用不可变引用重写示例,它就可以正常工作:
fn main() {
let env = Env {
parent: None,
val: "Global".to_string(),
};
ex_block(3, &env);
}
struct Env<'a> {
parent: Option<&'a Env<'a>>,
val: String,
}
impl Env<'_> {
fn walk_up(&self) {
println!("{}", self.val);
if let Some(env) = self.parent {
env.walk_up()
}
}
}
fn ex_block(further_levels: u8, env: &Env<'_>) {
if further_levels > 0 {
let child: Env = Env {
parent: Some(env),
val: format!("Level {}", further_levels),
};
ex_block(further_levels - 1, &child);
} else {
env.walk_up();
}
}
Run Code Online (Sandbox Code Playgroud)
正如我们所见,可变引用是不变的。这意味着对于某些类型T在整个生命周期内泛型,即使比 更窄,也&mut T<'a>不能强制转换为。由于持有 a ,因此它也是不变的。所以不能强行进入。&mut T<'b>'b'aEnv<'a>&mut Env<'a>Env<'a>Env<'b>
现在,代码中的第一个错误是由尝试强制&'r mut Env<'e>进入&'e mut Env<'e>(在分配 期间parent: Some(env))引起的,但没有'rover的界限'e,因此编译器会引发错误。
如果您ex_block稍微更改示例的函数,以便只有一个生命周期参数:
fn ex_block<'e>(further_levels: u8, env: &'e mut Env<'e>) {
if further_levels > 0 {
let mut child: Env = Env {
parent: Some(env),
val: format!("Level {}", further_levels),
};
ex_block(further_levels-1, &mut child);
} else {
env.walk_up();
}
}
Run Code Online (Sandbox Code Playgroud)
然后发生的情况是&mut child变量是 a &'child mut Env<'e>,'child是仅在分支范围内有效的局部子变量的生命周期if。同样,该变量无法强制转换为&'child mut Env<'child>,因此编译器会尝试强制'child转换为'e,以将变量转换为&'e mut Env<'e>。这导致编译器最终输出错误:
error[E0597]: `child` does not live long enough
--> src/main.rs:31:36
|
24 | fn ex_block<'e>(further_levels: u8, env: &'e mut Env<'e>) {
| -- lifetime `'e` defined here
25 | if further_levels > 0 {
26 | let mut child: Env = Env {
| --------- binding `child` declared here
...
31 | ex_block(further_levels-1, &mut child);
| ---------------------------^^^^^^^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `child` is borrowed for `'e`
32 | } else {
| - `child` dropped here while still borrowed
Run Code Online (Sandbox Code Playgroud)
编辑:修正了我的错误,我说&'a mut T不能强制执行&'b mut T,这是错误的。