2015-01-10 33 views
5

Khi tôi xác định một cấu trúc như thế này, tôi có thể vượt qua nó để một hàm theo giá trị mà không cần thêm bất cứ điều gì cụ thể:Tại sao đặc điểm Sao chép cần thiết cho khởi tạo mảng mặc định (có giá trị cấu trúc)?

#[derive(Debug)] 
struct MyType { 
    member: u16, 
} 

fn my_function(param: MyType) { 
    println!("param.member: {}", param.member); 
} 

Khi tôi muốn tạo một mảng của MyType trường với một giá trị mặc định

fn main() { 
    let array = [MyType { member: 1234 }; 100]; 
    println!("array[42].member: ", array[42].member); 
} 

Trình biên dịch Rust nói với tôi:

error[E0277]: the trait bound `MyType: std::marker::Copy` is not satisfied 
    --> src/main.rs:11:17 
    | 
11 |  let array = [MyType { member: 1234 }; 100]; 
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `MyType` 
    | 
    = note: the `Copy` trait is required because the repeated element will be copied 

Khi tôi thực hiện CopyClone, mọi thứ hoạt động:

impl Copy for MyType {} 
impl Clone for MyType { 
    fn clone(&self) -> Self { 
     MyType { 
      member: self.member.clone(), 
     } 
    } 
} 
  1. Tại sao tôi cần phải xác định một sản phẩm nào Copy thực hiện đặc điểm?

  2. Có cách nào đơn giản hơn để thực hiện việc này hoặc tôi có phải nghĩ lại điều gì đó không?

  3. Tại sao nó hoạt động khi chuyển một phiên bản MyType đến hàm theo giá trị? Tôi đoán là nó đang được di chuyển, vì vậy không có bản sao ở nơi đầu tiên.

Trả lời

18

Trái với C/C++, Rust có sự phân biệt rõ ràng giữa các loại được sao chép và di chuyển. Lưu ý rằng đây chỉ là một sự khác biệt ngữ nghĩa; về mức độ triển khai, hãy di chuyển bản sao tạm thời cạn, tuy nhiên, trình biên dịch đặt những hạn chế nhất định về những gì bạn có thể làm với các biến mà bạn đã chuyển từ đó.

Theo mặc định mọi loại chỉ có thể di chuyển (không thể sao chép). Nó có nghĩa là giá trị của các loại đó đều di chuyển xung quanh:

let x = SomeNonCopyableType::new(); 
let y = x; 
x.do_something();  // error! 
do_something_else(x); // error! 

Bạn thấy đấy, giá trị được lưu trữ trong x đã được chuyển đến y, và do đó bạn không thể làm bất cứ điều gì với x.

Di chuyển ngữ nghĩa là một phần rất quan trọng trong khái niệm quyền sở hữu trong Rust. Bạn có thể đọc thêm về nó in the official guide.

Một số loại, tuy nhiên, đủ đơn giản để bản sao của chúng cũng là bản sao ngữ nghĩa của chúng: nếu bạn sao chép giá trị byte-by-byte, bạn sẽ nhận được một giá trị hoàn toàn độc lập mới. Ví dụ, số nguyên thủy là các loại như vậy. Thuộc tính như vậy được chỉ định bằng Copy đặc điểm trong Rust, tức là nếu một loại thực hiện Copy thì giá trị của loại này hoàn toàn có thể sao chép được. Copy không chứa phương pháp; nó tồn tại chỉ để đánh dấu rằng các loại thực hiện có tài sản nhất định và do đó nó thường được gọi là một đặc điểm đánh dấu (cũng như vài đặc điểm khác làm những điều tương tự).

Tuy nhiên, nó không hoạt động với tất cả các loại. Ví dụ, các cấu trúc như vectơ được phân bổ động không thể tự động sao chép được: nếu chúng là, địa chỉ của phân bổ chứa trong chúng cũng sẽ được sao chép byte, và sau đó destructor của vectơ đó sẽ chạy hai lần trên cùng một phân bổ, gây ra điều này con trỏ được giải phóng hai lần, đó là một lỗi bộ nhớ.

Vì vậy, theo loại tùy chỉnh mặc định trong Rust không thể sao chép được.Nhưng bạn có thể chọn sử dụng cho nó bằng cách sử #[derive(Copy, Clone)] (hoặc, như bạn thấy, sử dụng trực tiếp impl; họ là tương đương, nhưng derive thường đọc tốt hơn):

#[derive(Copy, Clone)] 
struct MyType { 
    member: u16 
} 

(bắt nguồn Clone là cần thiết vì Copy thừa hưởng Clone, vì vậy tất cả mọi thứ đó là Copy cũng phải Clone)

Nếu loại của bạn có thể được tự động copyable về nguyên tắc, có nghĩa là, nó không có một destructor liên quan và tất cả các thành viên của nó được Copy, sau đó với derive loại của bạn cũng sẽ được Copy.

Bạn có thể sử dụng Copy loại trong mảng initializer chính vì mảng sẽ được khởi tạo với bản bytewise giá trị sử dụng trong initializer này, vì vậy loại của bạn có để thực hiện Copy để chỉ rằng nó thực sự có thể được tự động sao chép.

Ở trên là câu trả lời cho 1 và 2. Đối với 3, có, bạn hoàn toàn chính xác. Nó hoạt động chính xác vì giá trị được chuyển vào hàm. Nếu bạn cố gắng sử dụng biến số MyType sau khi bạn chuyển nó vào hàm, bạn sẽ nhanh chóng nhận thấy lỗi về việc sử dụng giá trị được di chuyển.

5

Tại sao tôi cần phải chỉ định triển khai Đặc điểm sao chép trống?

Copy là một built-in đặc điểm đặc biệt như vậy mà T thực hiện Copy đại diện cho rằng nó là an toàn để lặp lại một giá trị kiểu T với một bản sao byte cạn. Định nghĩa đơn giản này có nghĩa là người ta chỉ cần nói với trình biên dịch những ngữ nghĩa là chính xác, vì không có thay đổi cơ bản trong hành vi thời gian chạy: cả di chuyển (loại không phải là Copy) và một "bản sao" là byte nông bản sao, nó chỉ là một câu hỏi nếu nguồn có thể sử dụng sau này. Xem an older answer for more details.

(Trình biên dịch sẽ phàn nàn nếu nội dung của MyType không phải là Copy bản thân; trước đây nó sẽ được thực hiện tự động, nhưng tất cả đã thay đổi with opt-in built-in traits.)

Tạo một mảng được nhân đôi giá trị thông qua các bản sao cạn, và điều này được đảm bảo an toàn nếu TCopy. Nó là an toàn trong các tình huống tổng quát hơn, #5244 bao gồm một số trong số chúng, nhưng ở lõi, cấu trúc non-Copy sẽ không thể được sử dụng để tạo một mảng có độ dài cố định một cách tự động vì trình biên dịch không thể nói rằng sao chép là an toàn/chính xác.

Có cách nào đơn giản hơn để thực hiện việc này hay tôi phải nghĩ lại điều gì đó (tôi đến từ C)?

#[derive(Copy)] 
struct MyType { 
    member: u16 
} 

sẽ chèn thi trống thích hợp (#[derive] công trình với một số đặc điểm khác, ví dụ như người ta thường thấy #[derive(Copy, Clone, PartialEq, Eq)].)

Tại sao nó làm việc khi đi qua một thể hiện của MyType đến chức năng của giá trị?Tôi đoán là nó đang được di chuyển, vì vậy không có bản sao ở nơi đầu tiên.

Vâng, nếu không gọi hàm, bạn sẽ không thấy hành vi di chuyển so với bản sao (nếu bạn gọi nó hai lần cùng giá trị không phải là Copy, trình biên dịch sẽ phát ra lỗi về giá trị đã di chuyển). Tuy nhiên, "di chuyển" và "bản sao" về cơ bản giống nhau trên máy. Tất cả giá trị sử dụng theo giá trị của một giá trị là các bản sao nông ngữ theo nghĩa đen trong Rust, giống như trong C.

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