如何在 Rust 中实现任意加运算符?
How to implement arbitrary add operator in Rust?
我正在研究 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
。很难说这是 "correct" 答案,因为这里的选择可能不符合未说明的要求,但它 "works" 并且类似于您问题中的代码:
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
界限,它适用于大多数数字类型,这应该足够了。如果没有,您可以 Clone
代替,这更通用但效率可能较低。
如果两个参数可能有不同的类型,那么你将不得不引入另一个类型参数,S
,并约束T: Add<S, Output = T>
。在这一点上,我会再次质疑你在做什么,并建议你可能需要重新考虑你的整体设计,因为这一切都非常不生锈且令人困惑。
与其使用 dyn Any
,我强烈建议您考虑支持类型的枚举。代码可能更容易理解和调试,而且速度应该也更快。
我正在研究 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
。很难说这是 "correct" 答案,因为这里的选择可能不符合未说明的要求,但它 "works" 并且类似于您问题中的代码:
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
界限,它适用于大多数数字类型,这应该足够了。如果没有,您可以 Clone
代替,这更通用但效率可能较低。
如果两个参数可能有不同的类型,那么你将不得不引入另一个类型参数,S
,并约束T: Add<S, Output = T>
。在这一点上,我会再次质疑你在做什么,并建议你可能需要重新考虑你的整体设计,因为这一切都非常不生锈且令人困惑。
与其使用 dyn Any
,我强烈建议您考虑支持类型的枚举。代码可能更容易理解和调试,而且速度应该也更快。