2
votes

I've recently taken on learning Rust and I'm trying to write a small expression evaluator. I've been practicing Rust for a few days now and thought this task would be cool to work with Rust's Traits. What I tried to do is make Sum & Number structs implement Expression trait, so that I could express (pun unintended) (1 + 2) as an expression where left and right hand sides are expressions too. I've stumbled onto the problem that you can't just use Traits as properties' types, so you instead should use &dyn Trait or Box in the Book. Following this notion a rewrote it and now it compiles, but I can't get access to values inside Sum. Here's my code:

trait Expression {}
#[derive(Debug)]
struct Number {
    pub val: i32
}

impl Expression for Number {}

struct Sum {
    pub left: Box<dyn Expression>,
    pub right: Box<dyn Expression>
}

impl Expression for Sum {}

fn main() {
    let e = Sum{ left: Box::new(Number{ val: 2}),
                 right: Box::new(Number{ val: 2})
    };
    let sum = (2 + 2);
    println!("{:#?}", sum);
}

What I want to be able to do is get to Number's value:

e.left.val

and use nested constuctions like:

Sum{Sum{Number, Sum{Number, Number}}, Number}

I also tried to make explicit cast to Number:

let val = (e.left as Number).val;

But it fails with an error: non-primitive cast: std::boxed::Box<(dyn Expression + 'static)> as Number note: an as expression can only be used to convert between primitive types. Consider using the From trait.

Sorry for any language mistakes or messy explanation, English is not my first language.
I'm not an experienced programmer and very new to Rust so I would really appreciate any help, thanks!

2
Bottom line, Rust doesn't let you "cast" non-primitive types. You'll need to either implement the From trait, as mentioned by the compiler, or use Box::downcast to attempt to extract a Number from a Box<dyn Expression.Brian
@Brian I'm trying to implement From trait right now and still dont understand how to extract Number that lies in Box<dyn Expression> to construct new Number. My code for this is: ` impl From<Box<dyn Expression>> for Number { fn from(expr: Box<dyn Expression>) -> Self { //? Number { val: expr. } } } `georgie
From only makes sense if every Expression can be converted to a Number. Is this true for your design? Bear in mind, there is nothing stopping you from implementing Expression for, say, Vec. All your From implementation knows is that expr is an Expression trait object, which could be any type. Traits may be the wrong abstraction for what you are trying to accomplish.Brian
You should add a fn value (&self) -> i32 method to your Expression trait, then implement the proper behaviour for Sum and Number.Jmb
@Jmb yes, that worked, thanks :)georgie

2 Answers

0
votes

Rust doesn't let you cast non primitive types.

Reference: https://doc.rust-lang.org/rust-by-example/types/cast.html

0
votes

I think what you're trying to do is this (complete code in the playground):

trait Expression {
    fn evaluate(&self) -> i32;
}

impl Expression for Number {
    fn evaluate(&self) -> i32 {
        self.val
    }
}

impl Expression for Sum {
    fn evaluate(&self) -> i32 {
        self.left.evaluate() + self.right.evaluate()
    }
}