Rust:对向量中结构的多态调用

Rust: polymorphic calls for structs in a vector

我是 Rust 的新手,我正在尝试了解该语言的基础知识。

考虑以下特征

trait Function {
    fn value(&self, arg: &[f64]) -> f64;
}

和两个实现它的结构:

struct Add {}

struct Multiply {}

impl Function for Add {
    fn value(&self, arg: &[f64]) -> f64 {
        arg[0] + arg[1]
    }
}

impl Function for Multiply {
    fn value(&self, arg: &[f64]) -> f64 {
        arg[0] * arg[1]
    }
}

在我的 main() 函数中,我想将 AddMultiply 的两个实例分组到一个向量中,然后调用 value 方法。以下作品:

fn main() {
    let x = vec![1.0, 2.0];
    let funcs: Vec<&dyn Function> = vec![&Add {}, &Multiply {}];

    for f in funcs {
        println!("{}", f.value(&x));
    }
}

还有:

fn main() {
    let x = vec![1.0, 2.0];
    let funcs: Vec<Box<dyn Function>> = vec![Box::new(Add {}), Box::new(Multiply {})];

    for f in funcs {
        println!("{}", f.value(&x));
    }
}

有没有更好/更简洁的方法?我可以解决将实例包装在 Box 中的问题吗?在这种情况下,特征对象的要点是什么?

Is there any better / less verbose way?

确实没有办法使它不那么冗长。由于您使用的是特征对象,因此您需要告诉编译器向量的项是 dyn Function 而不是具体类型。编译器不能仅仅推断出您指的是 dyn Function 特征对象,因为 AddMultiply 都可以实现其他特征。

您也无法抽象出对 Box::new 的调用。为此,您必须以某种方式映射异构集合,这在 Rust 中是不可能的。但是,如果你经常写这个,你可以考虑为每个具体的 impl:

添加辅助构造函数
impl Add {
    fn new() -> Add {
        Add {}
    }

    fn new_boxed() -> Box<Add> {
        Box::new(Add::new())
    }
}

尽可能包含 new 构造函数是惯用的做法,但包含其他方便的构造函数也很常见。

这使得向量的构造变得不那么嘈杂:

let funcs: Vec<Box<dyn Function>> = vec!(Add::new_boxed(), Multiply::new_boxed()));

What is the takeaway with trait objects in this case?

使用动态调度总是会对性能造成小的影响。如果你所有的对象都是同一类型,它们可以密集地存放在内存中,这样迭代速度会快得多。一般来说,我不会太担心这个,除非你正在创建一个库箱,或者如果你真的想挤出最后一纳秒的性能。