3

我正在为 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() {}

如何更改代码以使其编译?

4

1 回答 1

4

这不是关于RefCellor的真正问题Ref。你有这个方法:

impl<'a> Enviroment<'a> {
    pub fn lookup(&self, name: &str) -> Option<Option<Value>>;
}

生命周期省略之后,这相当于:

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>>>;
}

这允许您的示例代码编译。

于 2017-03-15T15:03:29.983 回答