【问题标题】:How to implement arbitrary add operator in Rust?如何在 Rust 中实现任意加法运算符?
【发布时间】:2023-04-06 15:41:01
【问题描述】:

我正在使用 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()
}

我从编译器得到这样的错误:

错误:无法在 trait 对象上调用 downcast_ref 方法

很明显,编译器不知道如何将Any 转换为std::ops::Add

那么做这种事情的最佳做法是什么?

【问题讨论】:

  • 你能详细说明你的目标是什么吗?制作一个可以包装所有可能的Add 操作的函数,还是别的什么?使用RcAny 有要求吗?
  • 另外请提供minimal reproducible example。您提供的代码(添加了相关导入)对我产生了不同的错误:错误[E0277]:T 类型的值的大小在编译时无法知道
  • 您的意思是写T: Sized 而不是T: ?Sized

标签: generics rust traits dynamic-typing


【解决方案1】:

很明显,编译器不知道如何将 Any 转换为 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&lt;dyn Any&gt;,但你也有这个T参数,这只能意味着调用者知道这些@987654330的具体类型@ 参数,以便提供正确的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 绑定,它适用于大多数数字类型,应该足够了。如果没有,您可以改为 Clone,这更通用但可能效率较低。

如果两个参数可能有不同的类型,那么您将不得不引入另一个类型参数S,并约束T: Add&lt;S, Output = T&gt;。在这一点上,我会再次质疑您在做什么,并建议您可能需要重新考虑您的整体设计,因为这一切都非常不生锈且令人困惑。

我强烈建议您考虑支持类型的枚举,而不是使用dyn Any。代码可能更容易理解和调试,而且应该更快。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-05-06
    • 2022-11-26
    • 1970-01-01
    • 2020-03-31
    • 2013-01-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多