2017-07-17 45 views
9

Khi viết mã với những đặc điểm bạn có thể đặt các đặc điểm trong một đặc điểm ràng buộc:Sự khác biệt giữa <T: Trait> Hộp <T> và & Trait/Box <Trait> là gì?

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

Hoặc trực tiếp trong một loại Box hoặc tham khảo:

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

Những cung cấp cho các đầu ra tương tự. Vì vậy, sự khác biệt là gì?

(Câu hỏi này được lấy cảm hứng từ another question nơi đây chỉ là một trong một số khái niệm xen kẽ)

Trả lời

9

Với <T: Trait> Box<T> bạn đang sử dụng một đặc điểm chắc chắn sẽ nói với trình biên dịch mà bạn muốn có một Box với một thể hiện của một số loại T mà thực hiện Trait và bạn sẽ chỉ định T khi bạn sử dụng. Trình biên dịch Rust có thể sẽ tạo ra các mã khác nhau, hiệu quả, cho mỗi khác nhau T trong mã của bạn (monomorphization).

Với Box<Trait> bạn đang nói trình biên dịch mà bạn muốn có một Box với một đối tượng đặc điểm, một con trỏ đến một biết loại mà thực hiện Trait, có nghĩa là trình biên dịch sẽ sử dụng công văn năng động.

Tôi đã bao gồm hai ví dụ mà làm cho sự khác biệt rõ ràng hơn một chút:

<T: Trait> Box<T>, tức là đặc điểm ràng buộc:

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>, tức là đối tượng đặc điểm:

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

Để biết thêm chi tiết về sự khác biệt giữa giới hạn đặc điểm và đối tượng đặc điểm tôi đề nghị the section on trait objects in the first edition of the Rust book.

2

Quan trọng hơn, bạn không đã đặt kiểu chung chung đằng sau một tham chiếu (như & hoặc Box), bạn có thể chấp nhận nó trực tiếp:

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

fn main() { 
    myfunction3(5); 
} 

này có lợi ích tương tự của monomorphization mà không có sự nhược điểm của phân bổ bộ nhớ bổ sung (Box) hoặc cần giữ quyền sở hữu giá trị ở đâu đó (&).

Tôi muốn nói rằng generics thường phải là lựa chọn mặc định - bạn chỉ cần một đối tượng đặc điểm khi có tính năng động/không đồng nhất.

Các vấn đề liên quan