我想实现一个非常基本的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; 我真的不明白这个错误,也不知道怎么解决它。我尝试了其他一些变种,但他们没有解决这个问题。我的问题是:我做错了什么?
答案 0 :(得分:1)
(旁注:如果您使用Result
类型而不是其中包含布尔值的元组,这可能会更好。但我会坚持您为此答案编写它的方式。)< / p>
错误消息表明您无法使用移动的值t1
和t2
,因为当您取消引用这些值并对其进行匹配时,这些值会被移动。
t1
和t2
中存储的信息现在归比赛的那个分支中的变量a
,w
和t
所有。表达式,所以你必须使用它们。如果我错了,请纠正我。
因此,如果在行内
,你可以开始让你的榜样发挥作用(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
}
}
我现在是一个生锈的初学者,所以如果有人能帮助我理解为什么会这样,我会非常感激。