如何让 RefCell 的 Ref 活得够久
How to make RefCell's Ref live long enough
我正在为一种编程语言开发 Rust 解释器。一切都很顺利,直到我决定实施闭包,这引起了一些巨大的麻烦,因为现在每个闭包值都需要有一个可变的环境引用,它被定义在其中。我终于让它主要与 RefCell
一起工作,但是我现在 运行 又犯了一个我不知道如何解决的错误。
error: `e` does not live long enough
--> src/interpreter.rs:163:23
|
163 | let val = e.lookup(&name);
| ^ does not live long enough
...
169 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'b as defined on the body at 147:93...
--> src/interpreter.rs:147:94
|
147 | fn eval_expr<'a, 'b, 'c>(ast: &'a Expr, env: &'b RefEnv<'b>) -> Result<Value<'b>, Error<'c>> {
| ^
error: aborting due to previous error
相关代码如下:
use std::collections::HashMap;
use std::cell::RefCell;
#[derive(Clone, Debug)]
pub enum Value<'a> {
Number(f64),
UserFunc(Definition, Enviroment<'a>),
}
#[derive(Clone, Debug)]
pub struct Definition;
pub enum Expr {
Name(String),
}
// Nothing to do with the problem
pub enum Error {
UndefinedName(String),
}
#[derive(Debug, Clone)]
pub struct Enviroment<'a> {
current_frame: HashMap<String, Option<Value<'a>>>,
prev: Option<&'a Enviroment<'a>>,
}
impl<'a> Enviroment<'a> {
pub fn new() -> Enviroment<'a> {
Enviroment {
current_frame: HashMap::new(),
prev: None,
}
}
pub fn extend(bindings: Vec<(String, Value<'a>)>,
prev: Option<&'a Enviroment<'a>>)
-> Enviroment<'a> {
let mut frame = HashMap::new();
for (key, val) in bindings {
frame.insert(key, Some(val));
}
Enviroment {
current_frame: frame,
prev: prev,
}
}
pub fn lookup(&self, name: &str) -> Option<Option<Value>> {
let val = self.current_frame.get(&String::from(name));
if val.is_some() {
val.cloned()
} else {
if let Some(prev) = self.prev {
prev.lookup(name)
} else {
None
}
}
}
}
type RefEnv<'a> = RefCell<Enviroment<'a>>;
fn eval_expr<'a, 'b>(ast: &'a Expr, env: &'b RefEnv<'b>) -> Result<Value<'b>, Error> {
match *ast {
Expr::Name(ref name) => {
let e = env.borrow();
let val = e.lookup(&name);
if let Some(Some(v)) = val {
Ok(v)
} else {
Err(Error::UndefinedName(format!("{} is not defined", name)))
}
}
}
}
fn main() {}
如何更改代码使其通过编译?
关于 RefCell
或 Ref
这并不是真正的问题。你有这个方法:
impl<'a> Enviroment<'a> {
pub fn lookup(&self, name: &str) -> Option<Option<Value>>;
}
lifetime elision反转后,相当于:
impl<'a> Enviroment<'a> {
pub fn lookup<'b, 'c>(&'b self, name: &'c str) -> Option<Option<Value<'b>>>;
}
这意味着 Value
只保证包含与 Environment
一样长的值。你真正想要的是将 Value
绑定到任何 Environment
引用:
impl<'a> Enviroment<'a> {
pub fn lookup(&self, name: &str) -> Option<Option<Value<'a>>>;
}
这允许您的示例代码编译。
我正在为一种编程语言开发 Rust 解释器。一切都很顺利,直到我决定实施闭包,这引起了一些巨大的麻烦,因为现在每个闭包值都需要有一个可变的环境引用,它被定义在其中。我终于让它主要与 RefCell
一起工作,但是我现在 运行 又犯了一个我不知道如何解决的错误。
error: `e` does not live long enough
--> src/interpreter.rs:163:23
|
163 | let val = e.lookup(&name);
| ^ does not live long enough
...
169 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'b as defined on the body at 147:93...
--> src/interpreter.rs:147:94
|
147 | fn eval_expr<'a, 'b, 'c>(ast: &'a Expr, env: &'b RefEnv<'b>) -> Result<Value<'b>, Error<'c>> {
| ^
error: aborting due to previous error
相关代码如下:
use std::collections::HashMap;
use std::cell::RefCell;
#[derive(Clone, Debug)]
pub enum Value<'a> {
Number(f64),
UserFunc(Definition, Enviroment<'a>),
}
#[derive(Clone, Debug)]
pub struct Definition;
pub enum Expr {
Name(String),
}
// Nothing to do with the problem
pub enum Error {
UndefinedName(String),
}
#[derive(Debug, Clone)]
pub struct Enviroment<'a> {
current_frame: HashMap<String, Option<Value<'a>>>,
prev: Option<&'a Enviroment<'a>>,
}
impl<'a> Enviroment<'a> {
pub fn new() -> Enviroment<'a> {
Enviroment {
current_frame: HashMap::new(),
prev: None,
}
}
pub fn extend(bindings: Vec<(String, Value<'a>)>,
prev: Option<&'a Enviroment<'a>>)
-> Enviroment<'a> {
let mut frame = HashMap::new();
for (key, val) in bindings {
frame.insert(key, Some(val));
}
Enviroment {
current_frame: frame,
prev: prev,
}
}
pub fn lookup(&self, name: &str) -> Option<Option<Value>> {
let val = self.current_frame.get(&String::from(name));
if val.is_some() {
val.cloned()
} else {
if let Some(prev) = self.prev {
prev.lookup(name)
} else {
None
}
}
}
}
type RefEnv<'a> = RefCell<Enviroment<'a>>;
fn eval_expr<'a, 'b>(ast: &'a Expr, env: &'b RefEnv<'b>) -> Result<Value<'b>, Error> {
match *ast {
Expr::Name(ref name) => {
let e = env.borrow();
let val = e.lookup(&name);
if let Some(Some(v)) = val {
Ok(v)
} else {
Err(Error::UndefinedName(format!("{} is not defined", name)))
}
}
}
}
fn main() {}
如何更改代码使其通过编译?
关于 RefCell
或 Ref
这并不是真正的问题。你有这个方法:
impl<'a> Enviroment<'a> {
pub fn lookup(&self, name: &str) -> Option<Option<Value>>;
}
lifetime elision反转后,相当于:
impl<'a> Enviroment<'a> {
pub fn lookup<'b, 'c>(&'b self, name: &'c str) -> Option<Option<Value<'b>>>;
}
这意味着 Value
只保证包含与 Environment
一样长的值。你真正想要的是将 Value
绑定到任何 Environment
引用:
impl<'a> Enviroment<'a> {
pub fn lookup(&self, name: &str) -> Option<Option<Value<'a>>>;
}
这允许您的示例代码编译。