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:
Boxing each field inExpr::Add:Add { lhs: Box<Expr>, rhs: Box<Expr>, }Extracting
Expr::Addto a separate struct and thenBoxing 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 Boxing. That is, the number of Boxes 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?