3

我是 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()函数中,我想将Add 和的两个实例分组Multiply到一个向量中,然后调用该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));
    }
}

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

4

1 回答 1

1

有没有更好/不那么冗长的方法?

真的没有办法让它不那么冗长。由于您使用的是特征对象,因此您需要告诉编译器向量的项目是dyn Function而不是具体类型。编译器不能仅仅推断出你的意思是dyn Functiontrait 对象,因为可能还有其他的 traitAddMultiply实现了。

你不能抽象出对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()));

在这种情况下,特征对象的要点是什么?

使用动态调度总是会对性能造成小的影响。如果您的所有对象都是同一类型,则它们可以密集地打包在内存中,这可以更快地进行迭代。一般来说,除非您正在创建库板条箱,或者如果您真的想挤出最后纳秒的性能,否则我不会太担心这一点。

于 2018-12-22T17:28:29.897 回答