Enums are almost certainly the way to go here. Something like
enum Expr {
Literal(Literal),
Binary(Op, Box<Expr>, Box<Expr>),
//...
}
A value with type of Expr could be a literal or a binary expression. Binary expressions contain an operator and two other expressions (boxed to prevent infinite sized types).This is the building block for your tree
Other than enums, there is another way to achieve a sort of polymorphism in Rust similar to how it's used in that compiler book.
You can use Traits to encapsulate an idea and implement them on Structs. You can then use `dyn Trait` to represent a type erased dynamic instance of that trait (similar to how abstract class instances are represented).
This has some benefits in libraries, allowing external parties ability to implement their own instances of traits, but in a compiler where you will implement all possibilities, its not as useful. You also lose the ability to match on variants (instead having to resort to downcasting)