I have an enum Expr
:
enum Expr {
Const(i32),
Add {
lhs: Expr,
rhs: Expr,
},
}
If I compile this, I get the error telling me that I have recursive types without indirection. There are at least two ways of adding indirection:
Box
ing each field inExpr::Add
:Add { lhs: Box<Expr>, rhs: Box<Expr>, }
Extracting
Expr::Add
to a separate struct and thenBox
ing it in the enum variant:struct ExprAdd { lhs: Expr, rhs: Expr, } enum Expr { Const(i32), Add(Box<ExprAdd>), }
The first way of doing things introduces excessive Box
ing. That is, the number of Box
es is equal to the number of fields in the Expr::Add
variant and instantiating Expr::Add
is verbose:
let e = Expr::Add {
lhs: Box::new(Expr::Const(1)),
rhs: Box::new(Expr::Const(2)),
};
However, it allows for convenient pattern matching:
let e = Expr::Const(1);
match e {
Expr::Const(c) => {}
// Convenient destructuring.
Expr::Add { lhs, rhs } => {}
}
The second way of doing things is more efficient due to the usage of only one Box
but it is unnecessarily verbose(ExprAdd
struct is not really needed here) and pattern matching is limited:
let e = Expr::Const(1);
match e {
Expr::Const(c) => {}
// No convenient destructuring.
// `expr` is of type `ExprAdd`.
// Expr::Add(Box(ExprAdd { lhs, rhs })) => {} // fails because `Box` has private members.
Expr::Add(expr) => { /* use `expr.lhs` and `expr.rhs` */ }
}
Questions
- Which is better? Which is more idiomatic?
- Is there a way of declaring a recursive enum like in the second example that allows convenient pattern matching like in the first example?