2015-01-09 28 views
7

Biên dịch mã Rust sau đó sử dụng toán tử quá tảiđiều hành quá tải bởi kết quả giá trị sử dụng của giá trị chuyển

use std::ops::{Add}; 

#[derive(Show)] 
struct Point { 
    x: int, 
    y: int 
} 

impl Add for Point { 
    type Output = Point; 

    fn add(self, other: Point) -> Point { 
     Point {x: self.x + other.x, y: self.y + other.y} 
    } 
} 

fn main() { 
    let p: Point = Point {x: 1, y: 0}; 
    let pp = p + p; 
} 

Kết quả trong các lỗi biên dịch do quyền sở hữu của p:

<anon>:21:18: 21:19 error: use of moved value: `p` 
<anon>:21  let pp = p + p; 
         ^
<anon>:21:14: 21:15 note: `p` moved here because it has type `Point`, which is non-copyable 
<anon>:21  let pp = p + p; 
        ^

Lý do đằng sau nó được giải thích here và dẫn đến một số RFC không được chấp nhận (một phần do lý do của ví dụ trên). Tuy nhiên, sau RFC vẫn giới thiệu chữ ký loại giá trị cho các toán tử.

Mặc dù tôi hiểu lý do đằng sau quyết định. Do sự thiếu kinh nghiệm của tôi trong rỉ sét, tôi không chắc chắn cách "thích hợp" sẽ cho phép mã trên làm việc (a) nếu tôi không muốn sao chép hoặc (b) làm sao để cấu trúc có thể sao chép được?

Trả lời

4

Nếu bạn không muốn sao chép, theo như hiểu biết của newbie của tôi, bạn cần triển khai Add trên tham chiếu đến Point.

này sẽ được hỗ trợ bởi RFC:

May mắn thay, không có mất mát trong biểu cảm, vì bạn luôn có thể thực hiện các đặc điểm trên các loại tài liệu tham khảo. Tuy nhiên, đối với các loại mà cần phải được thực hiện bằng cách tham khảo, có một chút mất mát trong thái vì bạn có thể cần phải mượn một cách rõ ràng các toán hạng với &. Điều ngược lại là ngữ nghĩa quyền sở hữu trở nên rõ ràng hơn: chúng gần giống với các đối số hàm bình thường.

Và thực sự nó có vẻ làm việc:

use std::ops::{Add}; 

#[derive(Show)] 
struct Point { 
    x: i32, 
    y: i32 
} 

impl<'a> Add for &'a Point { 
    type Output = Point; 

    fn add(self, other: &'a Point) -> Point { //' 
     Point {x: self.x + other.x, y: self.y + other.y} 
    } 
} 

fn main() { 
    let p: Point = Point {x: 1, y: 0}; 
    let pp = &p + &p; 
    println!("{:?}", pp); 
} 

(playpen)

Để làm Point copyable thay vào đó, chỉ cần thay thế #[derive(Show)] với #[derive(Show,Copy)]. Các cấu trúc như vậy được sử dụng để có thể sao chép theo mặc định, nhưng nó là changed.

+1

Vấn đề với điều này là 'let pp = & p + & p + & p' không hoạt động. – SirVer

+0

@SirVer có, bạn sẽ phải viết một cái gì đó như 'let pp = & (& p + & p) + & p'. Tôi đoán rằng điều thực tế cần làm là tạo ra một số triển khai, giống như câu trả lời của Vladimir Matveev gợi ý (hoặc chỉ bắt nguồn từ 'Sao chép' và được thực hiện với nó). –

4

Nếu cấu trúc của bạn không thể sao chép được (ví dụ: có thực hiện Drop, hoặc cho một trong các trường của nó), thì có thể tạo ra một số triển khai: giá trị + giá trị, giá trị + tham chiếu, giá trị tham chiếu + và tham chiếu + tham chiếu. Ba đầu tiên có thể tái sử dụng lưu trữ của một trong các toán hạng, và cái cuối cùng có thể sao chép một trong các toán hạng và sau đó chỉ ủy quyền cho các hiện thực đã tồn tại. Bằng cách này, người dùng thư viện của bạn có thể dễ dàng quyết định xem họ có muốn sử dụng lại các giá trị hiện có để tối ưu hóa hay không.

Thực tế, đó là cách thực hiện Các loại BigInt hoặc Complex được xử lý.

Tuy nhiên, Point của bạn chỉ có thể được thực hiện Copy vì nó rẻ để sao chép.

+0

Vladimir, cảm ơn bạn đã trả lời. Điều gì xảy ra nếu loại của tôi dễ dàng, nhưng đắt tiền để sao chép, hãy nói loại 'Matrix1000x1000'?Trình biên dịch có đủ thông minh để tránh sao chép trong các hoạt động bị xiềng xích hay tôi cần viết bản tin nhỏ lẻ tới 4 triển khai mà bạn đã đề cập? – SirVer

+1

Tôi không chắc tôi hiểu câu hỏi của bạn. Nếu loại của bạn là tốn kém để sao chép, không làm cho nó 'Copy' và thực hiện các đặc điểm hoạt động cho bốn biến thể (self + self, & self + self, self + & self, & self + & self). Nếu loại của bạn lớn, bạn sẽ cần phải đặt dữ liệu trên heap, vì vậy di chuyển ngữ nghĩa sẽ đảm bảo rằng chỉ bản thân cấu trúc được sao chép. –

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