Reputation: 15
I'm working on an arbitrary expression evaluator in Rust,
Take Add
operator as an example:
fn eval_add<T: ?Sized + Add<T, Output=T>>(l: Rc<Any>, r: Rc<Any>) -> Rc<Any> {
l.downcast_ref::<Add<T, Output=T>>().unwrap() +
r.downcast_ref::<Add<T, Output=T>>().unwrap()
}
and I got such error from the compiler:
error: the
downcast_ref
method cannot be invoked on a trait object
It's obvious that the compiler doesn't know how to cast Any
to std::ops::Add
.
So what is the best practice to do such thing?
Upvotes: 0
Views: 376
Reputation: 58735
It's obvious that the compiler doesn't know how to cast Any to std::ops::Add.
That's because Add
is a trait and you may only downcast to a type.
This doesn't work:
l.downcast_ref::<Add<T, Output=T>>()
Because Add
is a trait, so this is really:
l.downcast_ref::<dyn Add<T, Output=T>>()
What you probably intended was just:
l.downcast_ref::<T>()
Since T
is the type variable in scope that implements Add
.
Your requirements are quite unclear, and the setup seems a little strange: you are passing around Rc<dyn Any>
, but you also have this T
parameter, which can only mean that that caller knows the concrete type of these dyn Any
arguments, in order to supply the correct T
. It's hard to say that this is the "correct" answer, because there are choices here that may not meet the unstated requirements, but it "works" and resembles the code in your question:
use std::rc::Rc;
use std::any::Any;
use std::ops::Add;
fn eval_add<T>(l: Rc<dyn Any>, r: Rc<dyn Any>) -> Rc<dyn Any>
where
T: Add<T, Output = T> + Copy + 'static
{
let l = *l.downcast_ref::<T>().unwrap();
let r = *r.downcast_ref::<T>().unwrap();
Rc::new(l + r)
}
Note that Add::add
takes its argument by value, so you must copy or clone it, since it is borrowed from the Rc
. I've added the Copy
bound, which applies to most numeric types, which should be sufficient. If not, you could Clone
instead, which is more general but potentially less efficient.
If the two arguments may have different types, then you'll have to introduce another type parameter, S
, and constrain T: Add<S, Output = T>
. At this point I would again question what you are doing and suggest that you might need to rethink your overall design, since this is all very un-Rusty and confusing.
Rather than using dyn Any
, I would strongly suggest that you consider an enum of supported types. The code will likely be easier to understand and debug, and should be faster too.
Upvotes: 3