Taking some code from my previous post’s first playground as an example, this match
pub fn exprt_to_expr<T: Expr>(exprt: &ExprT) -> T {
match &**exprt {
Lit(i) => T::lit(*i),
Add(x, y) => {
let xt: T = exprt_to_expr(x);
let yt: T = exprt_to_expr(y);
xt.add(&yt)
},
Mul(x, y) => {
let xt: T = exprt_to_expr(x);
let yt: T = exprt_to_expr(y);
xt.mul(&yt)
},
}
}
is (in less magical notation) the same as
pub fn exprt_to_expr<T: Expr>(exprt: &ExprT) -> T {
match &**exprt {
&Lit(ref i) => T::lit(*i),
&Add(ref x, ref y) => {
let xt: T = exprt_to_expr(x);
let yt: T = exprt_to_expr(y);
xt.add(&yt)
},
&Mul(ref x, ref y) => {
let xt: T = exprt_to_expr(x);
let yt: T = exprt_to_expr(y);
xt.mul(&yt)
},
}
}
where the &
pattern matches a “through a reference” and the “ref ident” pattern binds a value by-reference.
This could further be simplified by dropping the intermediate reference alltogether, i.e.
pub fn exprt_to_expr<T: Expr>(exprt: &ExprT) -> T {
// this still does not move or copy the ExprTInner from behind the `Rc`
// it can inspect the value in-place and `ref ident` patterns
// can reference the existing fields of the enum behind the `Rc`
match **exprt {
Lit(ref i) => T::lit(*i),
Add(ref x, ref y) => {
let xt: T = exprt_to_expr(x);
let yt: T = exprt_to_expr(y);
xt.add(&yt)
},
Mul(ref x, ref y) => {
let xt: T = exprt_to_expr(x);
let yt: T = exprt_to_expr(y);
xt.mul(&yt)
},
}
}
and also, in case of an integer like i
(or any Copy
type for that matter), you can also drop the ref
and the need to dereference (*i
) afterwards:
pub fn exprt_to_expr<T: Expr>(exprt: &ExprT) -> T {
match **exprt {
Lit(i) => T::lit(i),
Add(ref x, ref y) => {
let xt: T = exprt_to_expr(x);
let yt: T = exprt_to_expr(y);
xt.add(&yt)
},
Mul(ref x, ref y) => {
let xt: T = exprt_to_expr(x);
let yt: T = exprt_to_expr(y);
xt.mul(&yt)
},
}
}
There’s also a rule in Rust that return values of blocks are aways moved, which is why e.g.
pub fn exprt_to_expr<T: Expr>(exprt: &ExprT) -> T {
// note the extra braces
match {**exprt} {
Lit(i) => T::lit(i),
Add(ref x, ref y) => {
let xt: T = exprt_to_expr(x);
let yt: T = exprt_to_expr(y);
xt.add(&yt)
},
Mul(ref x, ref y) => {
let xt: T = exprt_to_expr(x);
let yt: T = exprt_to_expr(y);
xt.mul(&yt)
},
}
}
fails to compile
error[E0507]: cannot move out of an `Rc`
--> src/lib.rs:40:12
|
40 | match {**exprt} {
| ^^^^^^^ move occurs because value has type `ExprTInner`, which does not implement the `Copy` trait
(to be clear / avoid confusion here: just like in Haskell, when the compiler tells you that some type doesn’t implement some trait / type class, this doesn’t necessarily mean that you’re supposed to try implementing Copy
for ExprTInner
, even though you might feel encouraged to do so by the error message. Instead it means, in this case, that you shouldn’t try moving the whole ExprTInner
value in the first place.)