web-dev-qa-db-ja.com

Rustでゲッター/セッタープロパティを書き込む

Rust structimplを使用して簡単にするため)で使い始めたい非常にシンプルなゲッター/設定モデルを書いています。

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

このスニペットを実行すると、次のエラーが発生します。

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`.

私はRustが初めてなので、誰かが私に間違いを指摘できますか?

このスニペットをよりよく書くためのヒントも受け入れられます。私は常により簡単/より速い読みやすさを探しています。

15
ajm113

わかりました、ここでの特定の問題は、借用したコンテンツから移動できないことです。これは 回答済み多数 は言うまでもなく、さまざまな条件下で 章Rust Book の所有権について。

より興味深いのはゲッターとセッターについてです。はい、あなたはそれらをRustで書くことができますが、それらは最良の選択ではないかもしれません。

先に進む前に、絶対に理由がないことに注意したいと思いますゲッターで&mut selfを要求する...意図しない限り値の削除の一部として値を変更しますが、実際にはもうゲッターを処理していません。

次に、ゲッターでcloneしてはいけません。たとえば、ユーザーが値を読み取るだけの場合、これは非常に無駄です。不変の借用を返すことをお勧めします。これから、ユーザーは必要に応じてcloneifを実行できます。

とにかく、新しい値を検証するために何らかのロジックを実行する必要があるためにこれらを作成している場合は、セッターの使用を続けます。それ以外の場合は、次のようにすることができます。

#[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());
}

これにより、ユーザーはフィールドに直接アクセスできますが、実際にはフィールドに直接アクセスできません。これにより、新しい値を書き込むだけでなく、既存の値をインプレースで変更できます。これは、ヒープに割り当てられた大きなものにとって重要な場合があります。

さらに、他にもいくつか変更を加えました。

  • Defaultを機械的に派生させることができます。この場合、自分で書く理由はありません。

  • フィールドの場合、従来のスタイルはsnake_caseです。

  • あなたがPersonを作成した方法は、不必要に回り道でした。

23
DK.

ゲッターメソッドはselfを借用します。返品時self.name、使用できない借用参照からnameを移動しています。名前のコピーを返す必要があります。

また、内部構造体を変更しないため、ゲッターメソッドでselfへの可変参照を渡す必要はありません。

したがって、ゲッターメソッドは次のようになります。

fn get_first_name(&self) -> &String { &self.firstName }
fn get_last_name(&self) -> &String {  &self.lastName }
2
W.K.S