我正在研究 Rust 中的任意表达式求值器,
以Add
操作符为例:
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()
}
我从编译器那里得到这样的错误:
error: the
downcast_ref
method cannot be invoked on a trait object
很明显,编译器不知道如何将 Any
转换为 std::ops::Add
。
那么做这种事情的最佳实践是什么?
最佳答案
It's obvious that the compiler doesn't know how to cast Any to std::ops::Add.
那是因为Add
是一种特质,您只能向下转换为一种类型。
这行不通:
l.downcast_ref::<Add<T, Output=T>>()
因为 Add
是一个特质,所以这真的是:
l.downcast_ref::<dyn Add<T, Output=T>>()
您的意图可能只是:
l.downcast_ref::<T>()
自 T
是范围内实现 Add
的类型变量.
您的要求很不清楚,设置似乎有点奇怪:您正在传递 Rc<dyn Any>
, 但你也有这个 T
参数,这只能意味着调用者知道这些 dyn Any
的具体类型参数,以提供正确的 T
.很难说这是“正确”的答案,因为这里的选择可能不满足未说明的要求,但它“有效”并且类似于您问题中的代码:
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)
}
请注意 Add::add
按值获取其参数,因此您必须复制或克隆它,因为它是从 Rc
借来的.我添加了 Copy
bound,适用于大多数数值类型,应该够用了。如果没有,你可以 Clone
相反,它更通用但效率可能较低。
如果两个参数可能有不同的类型,那么你将不得不引入另一个类型参数,S
, 并约束 T: Add<S, Output = T>
.在这一点上,我会再次质疑你在做什么,并建议你可能需要重新考虑你的整体设计,因为这一切都非常 un-Rusty 和令人困惑。
而不是使用 dyn Any
,我强烈建议您考虑支持类型的枚举。代码可能会更容易理解和调试,而且速度也应该更快。
关于generics - 如何在 Rust 中实现任意加运算符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55097693/