Rust中无需Trait实现Env结构体持有同类型引用的问题
如何定义带可变父环境引用的Env结构体
我需要定义一个名为Env的结构体,它可以包含一个对另一个Env的可变引用(称为parent)。在所有实际场景中,我能保证父Env的生命周期长于引用它的子Env,但不知道怎么在Rust类型系统里表达这一点。
首次尝试
最初写的代码如下:
struct Env<'a> { parent: Option<&'a mut Env<'a>> }
这段代码能编译,但它强制父、子Env共享同一个生命周期参数,可实际中子Env的生命周期比父Env短,很快就会触发错误。
期望的逻辑
我想表达的逻辑大概是这样:
struct Env<'r> { parent: Option<&'r mut Env<'e>> where 'e : 'r }
也就是父Env的生命周期'e要长于子Env持有的引用生命周期'r,但添加第二个生命周期参数后又回到了僵局。
失败示例
我正在学习《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 { /* 错误:该行要求'r必须比'e长,但这不可能成立 */ parent: Some(env), val: format!("Level {}", further_levels), }; ex_block(further_levels-1, &mut child); } else { env.walk_up(); } }
为什么不选择其他方案?
为何不让Env拥有父环境的所有权?
当子环境生命周期结束后,原父环境仍需要保留,这正是借用的适用场景,所有权转移不符合需求。
为何不将所有Env存入内存池?
很多树结构资料建议用Vec存储节点并使用索引替代引用,但这只是通过模拟引用规避问题,安全性不如C++智能指针,我不想这么做。
为何不使用Trait技巧?
尝试过类似方案,结果触发了递归实例化超出限制的错误:
error: reached the recursion limit while instantiating
ex_block::<Env<Env<Env<Env<Env<E...>>>>>>>>>>>>>>>>>>>>
使用dyn Trait虽然可行,但会引入运行时函数查找,我希望避免这种开销:
struct Env<'a> { parent: Option<&'a mut dyn EnvTrait>, }
内容的提问来源于stack exchange,提问作者Andrew Cramer
相关产品推荐
相关产品推荐

