2016-03-26 21 views
8

Có một ví dụ tuyệt vời về ngữ nghĩa di chuyển của Rust được ghi ở đây: Rust Move Semantics trên trang web Rust By Example.Rust di chuyển các biến ngăn xếp không thể sao chép được như thế nào?

Tôi có hiểu biết cơ bản về cả hai trường hợp được chứng minh. Việc đầu tiên là làm thế nào một nguyên thủy có thể có một bí danh mới và bản gốc vẫn có thể được sử dụng bởi vì kết quả cuối cùng là một bản sao nhìn thấy như i32 sử dụng các đặc điểm Copy. Điều này có ý nghĩa tốt với tôi.

Ngoài ra, vì nhiều lý do tốt, ví dụ thứ hai có ý nghĩa về việc có nhiều bí danh tham chiếu đến số i32 trên heap. Rust thực thi các quy tắc quyền sở hữu và do đó, bí danh ban đầu không thể được sử dụng ngay bây giờ khi một ràng buộc mới đã được tạo. Điều này giúp ngăn chặn các cuộc đua dữ liệu, giải phóng đôi, v.v.

Nhưng dường như có trường hợp thứ ba không được nói đến. Làm thế nào để Rust thực hiện di chuyển các cấu trúc phân bổ ngăn xếp không thực hiện đặc điểm Copy? này được minh họa bằng đoạn mã sau:

#[derive(Debug)] 
struct Employee{ 
    age: i32, 
} 

fn do_something(m: Employee){ 
    println!("{:?}", m); 
} 

fn main() { 
    let x = Employee { 
     age: 25, 
    }; 

    do_something(x); 

    //compiler error below because x has moved 
    do_something(x); 
} 

này tôi biết: Trong trường hợp trên, Rust sẽ phân bổ các Employee trên stack. Cấu trúc trên không thực hiện đặc điểm Copy và do đó sẽ không được sao chép khi được gán cho bí danh mới. Điều này rất khó hiểu với tôi bởi vì nếu cấu trúc Employee được phân bổ trên ngăn xếp và cũng không thực hiện đặc điểm Copy ở đâu/nó di chuyển như thế nào? Liệu nó có được di chuyển đến khung ngăn xếp của do_something() không?

Bất kỳ trợ giúp nào được đánh giá cao trong việc giải thích câu hỏi hóc búa này.

+1

Bạn có muốn đơn giản hóa ví dụ của mình không? Sẽ là tuyệt vời để làm cho 'Employee' struct ít phức tạp hơn và ít nhất là để loại bỏ tuổi thọ. 'struct Employee {age: i32}' sẽ là đủ, ví dụ. –

+0

@LukasKalbertodt - vâng tôi đã đơn giản hóa ví dụ. –

Trả lời

6

Sản phẩm có được di chuyển đến khung ngăn xếp của do_something() không?

Có. Các loại không phải là Copy được di chuyển về mặt vật lý giống hệt như Copy các loại: với memcpy. Bạn đã hiểu rằng nguyên mẫu Copy -types được sao chép vào vị trí mới (khung ngăn xếp mới chẳng hạn) byte-by-byte.

Bây giờ xem xét thực hiện này của Box:

struct Box<T> { 
    ptr: *const T, 
} 

Khi bạn có

let b = Box::new(27i32); 
do_something(b); // `b` is moved into `do_something` 

sau đó một i32 được cấp phát trên heap và Box tiết kiệm con trỏ thô với đống phân bổ bộ nhớ. Lưu ý rằng trực tiếp Box (con trỏ thô bên trong) là trực tiếp trên ngăn xếp, không phải trên heap! Chỉ cần i32 là trên heap.

Khi di chuyển Box, nó là memcpy ed, như tôi vừa nói. Điều này có nghĩa là nội dung ngăn xếp được sao chép (!!) ... do đó chỉ cần con trỏ được sao chép từng byte một. Không có phiên bản thứ hai của i32!

Không có sự khác biệt giữa các loại Copy và không phải Copy khi di chuyển về mặt vật lý. Sự khác biệt duy nhất là trình biên dịch thực thi các quy tắc khác nhau đối với các loại đó.

+4

Nitơ nhỏ: AIUI, không đảm bảo rằng các giá trị sẽ * thực sự * được di chuyển, nhưng ngữ nghĩa là như vậy mà bạn phải giả định rằng chúng là. Ví dụ, trình biên dịch được tự do viết lại 'do_something' để chấp nhận tham chiếu đến' b' để ngăn chặn các bit sao chép xung quanh. Điều quan trọng là mã hoạt động * như thể * các bit đã được di chuyển. – Shepmaster

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