【问题标题】:What is the difference between <T: Trait> Box<T> and &Trait / Box<Trait>?<T: Trait> Box<T> 和 &Trait / Box<Trait> 有什么区别?
【发布时间】:2017-07-17 19:02:39
【问题描述】:

在编写带有特征的代码时,您可以将特征放入特征绑定中:

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,这只是几个混合概念之一)

【问题讨论】:

    标签: rust traits trait-objects


    【解决方案1】:

    使用&lt;T: Trait&gt; Box&lt;T&gt;,您正在使用一个特征绑定来告诉编译器您想要一个Box,该实例具有某种类型的T,它实现了Trait,并且您将在使用它时指定T . Rust 编译器可能会为您代码中的每个不同的T 创建不同的、高效的代码(单态化)。

    使用Box&lt;Trait&gt;,你告诉编译器你想要一个带有特征对象的Box,一个指向实现Traitunknown类型的指针,这意味着编译器将使用动态调度。

    我已经包含了两个示例,这使得区别更加清晰:

    &lt;T: Trait&gt; Box&lt;T&gt;,即特征绑定:

    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&lt;Trait&gt;,即特征对象:

    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"));
    }
    

    有关 trait bounds 和 trait 对象之间区别的更多详细信息,我推荐the section on trait objects in the first edition of the Rust book

    【讨论】:

      【解决方案2】:

      重要的是,您不必必须将泛型类型放在引用后面(如&amp;Box),您可以直接接受:

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

      这与单态化具有相同的好处,而没有额外内存分配 (Box) 或需要在某处保留值的所有权 (&amp;) 的缺点。

      我想说泛型通常应该是默认选择——当存在动态调度/异构时,您只需要一个 trait 对象

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-04-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多