<t:trait =“”> Box <t>和&amp; Trait / Box <trait>有什么区别?

时间:2017-07-17 19:02:39

标签: rust traits trait-objects

编写具有特征的代码时,可以将特征置于特征界限中:

use std::fmt::Debug;

fn myfunction1<T: Debug>(v: Box<T>) {
    println!("{:?}", v);
}

fn myfunction2<T: Debug>(v: &T) {
    println!("{:?}", v);
}

fn main() {
    myfunction1(Box::new(5));
    myfunction2(&5);
}

或直接使用Box或引用类型:

use std::fmt::Debug;

fn myfunction3(v: Box<Debug>) {
    println!("{:?}", v);
}

fn myfunction4(v: &Debug) {
    println!("{:?}", v);
}

fn main() {
    myfunction3(Box::new(5));
    myfunction4(&5);
}

这些输出相同。那有什么区别?

(这个问题的灵感来自another question,这只是几个混合概念中的一个)

2 个答案:

答案 0 :(得分:10)

使用<T: Trait> Box<T>,您正在使用特征绑定告诉编译器您希望Box具有实现T的某种类型Trait的实例,并且您将使用时指定T。 Rust编译器可能会为代码中的每个T创建不同的,高效的代码(单态化)。

使用Box<Trait>,您告诉编译器您希望Box具有特征对象,指向实现Trait未知类型的指针,意味着编译器将使用动态调度。

我已经包含了两个让差异更清晰的例子:

<T: Trait> Box<T>,即特质限制:

use std::fmt::Debug;

struct Wrapper<T> {
    contents: Option<Box<T>>,
}

impl<T: Debug> Wrapper<T> {
    fn new() -> Wrapper<T> {
        Wrapper { contents: None }
    }

    fn insert(&mut self, val: Box<T>) {
    }
}

fn main() {
    let mut w = Wrapper::new();

    // makes T for w be an integer type, e.g. Box<i64>
    w.insert(Box::new(5));

    // type error, &str is not an integer type
    // w.insert(Box::new("hello"));
}

Box<Trait>,即特质对象:

use std::fmt::Debug;

struct Wrapper {
    contents: Option<Box<Debug>>,
}

impl Wrapper {
    fn new() -> Wrapper {
        Wrapper { contents: None }
    }

    fn insert(&mut self, val: Box<Debug>) {
    }
}

fn main() {
    let mut w = Wrapper::new();
    w.insert(Box::new(5));
    w.insert(Box::new("hello"));
}

有关特质边界与特质对象之间差异的更多详情,我建议the section on trait objects in the first edition of the Rust book

答案 1 :(得分:4)

重要的是,您没有 将通用类型放在引用后面(例如&Box),您可以直接接受它:

fn myfunction3<T: Debug>(v: T) {
    println!("{:?}", v);
}

fn main() {
    myfunction3(5);
}

这具有单形化的相同优点,没有额外内存分配的缺点(Box)或需要在某处保持值的所有权(&)。

我认为泛型通常应该是默认选择 - 当存在动态调度/异构时,您只需要特征对象

相关问题