2017-07-17 33 views
9

Podczas pisania kodu z cech można umieścić cechę w cecha związana:Jaka jest różnica między <T: Trait> Box <T> i & Trait/Box <Trait>?

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

lub bezpośrednio w rodzaju Box lub odniesienia:

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

one dają ten sam wynik. Jaka jest różnica?

(ta kwestia była inspirowana przez another question gdzie to tylko jeden z kilku wymieszanych pojęć)

Odpowiedz

9

Z <T: Trait> Box<T> używasz cechę związaną poinformować kompilator, że chcesz Box z instancją pewnego rodzaju T który implementuje Trait, a podczas korzystania z niego określisz T. Kompilator Rust prawdopodobnie stworzy inny, wydajny kod dla każdego innego kodu T (monomorfizacja).

Z Box<Trait> mówisz kompilator, że chcesz Box z obiektem cechy, wskaźnik do nieznanego typu, które implementujeTrait, co oznacza, że ​​kompilator użyje dynamiczną wysyłkę.

podaję dwa przykłady, które czyni różnicę nieco jaśniejszy:

<T: Trait> Box<T>, czyli cechach związany:

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>, czyli cecha obiektu:

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

do dalszego szczegóły dotyczące różnicy między granicami cech a obiektami cech, które polecam the section on trait objects in the first edition of the Rust book.

2

Co ważne, nie mają umieścić typ rodzajowy za punkt odniesienia (jak & lub Box), można go przyjąć bezpośrednio:

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

fn main() { 
    myfunction3(5); 
} 

ten ma te same zalety monomorphization bez minusem dodatkowej alokacji pamięci (Box) lub konieczności zachowania własności wartości gdzieś (&).

Powiedziałbym, że leki generyczne powinny być często wybierane domyślnie - wymagany jest tylko obiekt cechowy w przypadku dynamicznej wysyłki/niejednorodności.