2016-02-14 19 views
5

Tôi đang viết một mô hình thiết lập/khởi động rất đơn giản mà tôi muốn bắt đầu sử dụng trong Rust vì các lý do đơn giản khi sử dụng structimpl.Viết thuộc tính getter/setter trong Rust

struct Person { 
    firstName: String, 
    lastName: String, 
} 

impl Person { 
    fn get_first_name(&mut self) -> String { return self.firstName; } 
    fn get_last_name(&mut self) -> String { return self.lastName; } 

    fn set_first_name(&mut self, x: String) { self.firstName = x; } 
    fn set_last_name(&mut self, x: String) { self.lastName = x; } 

    fn default() -> Person { 
     Person {firstName: "".to_string(), lastName: "".to_string()} 
    } 
} 

fn main() { 
    let mut my_person : Person = Person{ ..Person::default() }; 

    my_person.set_first_name("John".to_string()); 
    my_person.set_last_name("Doe".to_string()); 

    println!("{}", my_person.firstName); 
    println!("{}", my_person.lastName); 
} 

Khi tôi chạy đoạn mã này, tôi nhận được lỗi sau.

src\main.rs:7:53: 7:57 error: cannot move out of borrowed content [E0507] 
src\main.rs:7  fn get_first_name(&mut self) -> String { return self.firstName; } 
                    ^~~~ 
src\main.rs:8:53: 8:57 error: cannot move out of borrowed content [E0507] 
src\main.rs:8  fn get_last_name(&mut self) -> String { return self.lastName; } 
                    ^~~~ 
error: aborting due to 2 previous errors 
Could not compile `sandbox`. 

Ai đó có thể chỉ ra nhầm lẫn với tôi vì tôi rất mới với Rust?

Các mẹo viết đoạn mã này tốt hơn cũng sẽ được chấp nhận. Tôi luôn tìm kiếm khả năng đọc dễ dàng hơn/nhanh hơn.

+3

Tại sao bạn nghĩ rằng bạn cần getters và setters? Tại sao không 'struct Person {pub first_name: String, pub last_name: String,}' mà là khá đơn giản? –

Trả lời

1

Phương thức getter của bạn mượn self. Khi bạn quay trở lại self.name, bạn đang di chuyển name từ tài liệu tham khảo được mượn không được phép. Bạn nên trả lại một bản sao của tên.

Ngoài ra, bạn không cần chuyển một tham chiếu có thể thay đổi đến self trong các phương thức getter vì bạn không sửa đổi cấu trúc bên trong.

Do đó, phương pháp getter của bạn nên là:

fn get_first_name(&self) -> &String { &self.firstName } 
fn get_last_name(&self) -> &String { &self.lastName } 
+0

Xin cảm ơn vì nguồn cấp dữ liệu rất hữu ích! Làm cho tinh thần để làm tất cả mọi thứ trực tiếp, bây giờ bạn đặt nó theo cách đó. Rõ ràng tôi đến từ một nền OOP rất nặng với PHP/Java. Vì vậy, trong trường hợp này tôi cần phải bắt đầu suy nghĩ đơn giản hơn chức năng trong trường hợp này là khá nhiều những gì tôi cần phải làm cho hầu hết các phần. Trừ khi chúng ta đang nói về việc thêm chức năng cho cấu trúc. – ajm113

10

Ok, vấn đề cụ thể ở đây là không có khả năng di chuyển ra khỏi nội dung vay mượn. Điều này đã được answerednumeroustimesbefore trong nhiều điều kiện khác nhau, chưa kể đến the chapter on the subject of ownership in the Rust Book.

Điều thú vị hơn là về getters và setters. Có, bạn có thể viết chúng trong Rust, nhưng chúng có thể không phải là lựa chọn tốt nhất.

Trước khi tiếp tục, tôi chỉ muốn lưu ý rằng có hoàn toàn không có lý do yêu cầu &mut self trên getter ... trừ khi bạn có ý định sửa đổi giá trị như một phần của việc xóa giá trị, nhưng sau đó bạn không thực sự giao dịch với một getter nữa.

Thứ hai, bạn không đượcclone trong bộ nạp. Điều này là cực kỳ lãng phí nếu tất cả người dùng muốn làm là, ví dụ, đọc từ giá trị. Tốt hơn là trả lại khoản vay không thể thay đổi, từ đó người dùng có thể clonenếu họ cần.

Dù sao, nếu bạn đang viết chúng vì bạn muốn chạy một số loại logic để xác thực các giá trị mới, hãy tiếp tục sử dụng các bộ định vị. Nếu không, bạn có thể làm một cái gì đó như thế này:

#[derive(Default)] 
struct Person { 
    first_name: String, 
    last_name: String, 
} 

impl Person { 
    // Immutable access. 
    fn first_name(&self) -> &String { 
     &self.first_name 
    } 
    fn last_name(&self) -> &String { 
     &self.last_name 
    } 

    // Mutable access. 
    fn first_name_mut(&mut self) -> &mut String { 
     &mut self.first_name 
    } 
    fn last_name_mut(&mut self) -> &mut String { 
     &mut self.last_name 
    } 
} 

fn main() { 
    let mut my_person = Person::default(); 

    *my_person.first_name_mut() = String::from("John"); 
    *my_person.last_name_mut() = "Doe".into(); 

    println!("first_name: {}", my_person.first_name()); 
    println!("last_name: {}", my_person.last_name()); 

    // Can't do this efficiently with getter/setter! 
    { 
     let s = my_person.last_name_mut(); 
     s.truncate(2); 
     s.push('w'); 
    } 

    println!("first_name: {}", my_person.first_name()); 
    println!("last_name: {}", my_person.last_name()); 
} 

này mang lại cho người dùng nhiều hơn hoặc ít hơn truy cập trực tiếp đến các lĩnh vực, mà không thực sự cho họ truy cập trực tiếp đến các lĩnh vực. Ngoài việc viết các giá trị mới, điều này cũng cho phép người dùng thay đổi các giá trị hiện tại tại chỗ, điều này có thể quan trọng đối với những thứ lớn, được cấp phát đống.

Bên cạnh đó, tôi thực hiện một vài thay đổi khác:

  • Bạn có thể chỉ cách máy móc lấy Default; không có lý do nào trong trường hợp này để tự viết nó.

  • Kiểu thông thường là snake_case cho trường.

  • Cách bạn tạo Person là vòng xoay không cần thiết.

+0

Sử dụng '& str' thay vì' & String' có đúng không? –

+0

@ W.K.S, funny Tôi thực sự có cùng một câu hỏi. Tôi nhớ đọc một cái gì đó dọc theo dòng sử dụng str thay vào đó, nhưng tôi không nhớ ... – ajm113

+0

@ ajm113 Thông thường, có. * Tuy nhiên *, trong trường hợp này, bạn * cũng * có thể truy cập mutable vào 'String', và có một thứ bạn có thể làm với' & String' mà bạn không thể làm với '& str': kiểm tra dung lượng. Plus, * nói chung *, hai con trỏ sẽ cùng loại. –

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