什么是穿越" ADT"在Rust?

时间:2016-01-10 06:19:44

标签: rust

我想实现一个非常基本的lambda减速器。出现的第一个问题是用什么类型的数据类型来实现AST? In Haskell this would be an ADT。在Rust中,似乎正确使用枚举和Box es:

enum Term{
    Num(i64),
    Plus(Box<Term>, Box<Term>),
    Var(i64),
    Lam(i64, Box<Term>),
    App(Box<Term>, Box<Term>)
}

这似乎是一个不错的选择,但由于我是Rust的新手,很可能我的问题 - 接下来 - 只是我选择了错误的数据类型,如果我选择了正确的数据类型表示,我的问题就不复存在了。如果情况确实如此,请告诉我!

现在减少一步。在Haskell代码引用之后,我们最终会得到类似的东西:

fn reduceOneStep(t: Term) -> (Term, bool) {
    match t {
        Term::Num(a) => (t, false),
        Term::Plus(t1, t2) =>
        match (*t1, *t2) {
            (Term::Num(a), Term::Num(b)) => (Term::Num(a + b), true),
            (Term::Num(a), w) =>
            match reduceOneStep(w) {
              (t, b) => if b { (Term::Plus(t1, Box::new(t)), true) } else { (Term::Plus(t1, t2), false) }
            },
            _ => (Term::Num(1), false) //ignore .. this is just to satisfy typing and totality
        },
        x => (Term::Num(1), false) //ignore .. this is just to satisfy typing and totality
    }
}

然而,行

(t, b) => if b { (Term::Plus(t1, Box::new(t)), true) } else { (Term::Plus(t1, t2), false) }

编译失败。原因是我使用了移动值​​t1&#34; 我真的不明白这个错误,也不知道怎么解决它。我尝试了其他一些变种,但他们没有解决这个问题。我的问题是:我做错了什么?

1 个答案:

答案 0 :(得分:1)

(旁注:如果您使用Result类型而不是其中包含布尔值的元组,这可能会更好。但我会坚持您为此答案编写它的方式。)< / p>

错误消息表明您无法使用移动的值t1t2,因为当您取消引用这些值并对其进行匹配时,这些值会被移动。

t1t2中存储的信息现在归比赛的那个分支中的变量awt所有。表达式,所以你必须使用它们。如果我错了,请纠正我。

因此,如果在行内

,你可以开始让你的榜样发挥作用
(t, b) => if b { (Term::Plus(t1, Box::new(t)), true) } else { (Term::Plus(t1, t2), false) }

您将t1替换为Box::new(Term::Num(a)),将t2替换为Box::new(w)。添加了一些缩进的是:

(t, b) => if b {
  (Term::Plus(Box::new(Term::Num(a)), Box::new(t)), true)
} else {
  (Term::Plus(Box::new(Term::Num(a)), Box::new(w)), false)
}

这也失败了,因为来电reduceOneStep(w)获得了w的所有权。这可以通过使reduceOneStep借用其参数来解决:

fn reduceOneStep(t: &Term) -> (Term, bool) {
  match t {
    &Term::Num(a) => (*t, false),
    &Term::Plus(t1, t2) =>
    match (*t1, *t2) {
      (Term::Num(a), Term::Num(b)) => (Term::Num(a + b), true),
      (Term::Num(a), w) =>
      match reduceOneStep(&w) {
        (t, b) => if b {
          (Term::Plus(Box::new(Term::Num(a)), Box::new(t)), true)
        } else {
          (Term::Plus(Box::new(Term::Num(a)), Box::new(w)), false)
        }
      },
      _ => (Term::Num(1), false) //ignore .. this is just to satisfy typing and totality
    },
    x => (Term::Num(1), false) //ignore .. this is just to satisfy typing and totality
  }
}

但这有更多错误,说cannot move out of borrowed content,指向它返回*t的位置。这是因为它既不能将借款退还给所有者,又会返回其中的一部分,因为一个可以被释放而另一个可以被搁置。解决此问题的一种方法是#[derive(Clone)]枚举Term,然后使用:

fn reduceOneStep(t: &Term) -> (Term, bool) {
  match t {
    &Term::Num(a) => (t.clone(), false),
    &Term::Plus(t1, t2) =>
    match (*t1, *t2) {
      (Term::Num(a), Term::Num(b)) => (Term::Num(a + b), true),
      (Term::Num(a), w) =>
      match reduceOneStep(&w) {
        (t, b) => if b {
          (Term::Plus(Box::new(Term::Num(a)), Box::new(t)), true)
        } else {
          (Term::Plus(Box::new(Term::Num(a)), Box::new(w.clone())), false)
        }
      },
      _ => (Term::Num(1), false) //ignore .. this is just to satisfy typing and totality
    },
    x => (Term::Num(1), false) //ignore .. this is just to satisfy typing and totality
  }
}

但是这仍然有同样的错误。呵呵。错误消息在它下面有这个提示:help: to prevent the move, use `ref t1` or `ref mut t1` to capture value by reference。然后在修复了一些不匹配的类型并摆弄了盒子derefs并借用之后,我终于得到了这个工作:

fn reduceOneStep(t: &Term) -> (Term, bool) {
  match t {
    &Term::Num(a) => (t.clone(), false),
    &Term::Plus(ref t1, ref t2) =>
    match (&**t1, &**t2) {
      (&Term::Num(a), &Term::Num(b)) => (Term::Num(a + b), true),
      (&Term::Num(a), w) =>
      match reduceOneStep(&w) {
        (t, b) => if b {
          (Term::Plus(Box::new(Term::Num(a)), Box::new(t)), true)
        } else {
          (Term::Plus(Box::new(Term::Num(a)), Box::new(w.clone())), false)
        }
      },
      _ => (Term::Num(1), false) //ignore .. this is just to satisfy typing and totality
    },
    x => (Term::Num(1), false) //ignore .. this is just to satisfy typing and totality
  }
}

我现在是一个生锈的初学者,所以如果有人能帮助我理解为什么会这样,我会非常感激。