What is the difference between Box and &Trait / Box?

后端 未结 2 1998
南旧
南旧 2020-12-20 13:23

When writing code with traits you can put the trait in a trait bound:

use std::fmt::Debug;

fn myfunction1(v: Box) {
    println!(\"         


        
相关标签:
2条回答
  • 2020-12-20 13:51

    With <T: Trait> Box<T> you are using a trait bound to tell the compiler that you want a Box with an instance of some type T which implements Trait, and you will specify T when you use it. The Rust compiler will likely create different, efficient, code for each different T in your code (monomorphization).

    With Box<Trait> you are telling the compiler that you want a Box with a trait object, a pointer to an unknown type which implements Trait, which means that the compiler will use dynamic dispatch.

    I've included two examples which makes the difference a bit clearer:

    <T: Trait> Box<T>, i.e. trait bound:

    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>, i.e. trait object:

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

    For further details on the difference between trait bounds and trait objects I recommend the section on trait objects in the first edition of the Rust book.

    0 讨论(0)
  • 2020-12-20 13:59

    Importantly, you don't have to put the generic type behind a reference (like & or Box), you can accept it directly:

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

    This has the same benefits of monomorphization without the downside of additional memory allocation (Box) or needing to keep ownership of the value somewhere (&).

    I would say that generics should often be the default choice — you only require a trait object when there is dynamic dispatch / heterogeneity.

    0 讨论(0)
提交回复
热议问题