2016-02-14 6 views
5

Sto scrivendo un modello getter/impostazione molto semplice che vorrei iniziare a utilizzare in Rust per motivi di semplicità utilizzando struct e impl.Proprietà getter/setter di scrittura in 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); 
} 

Quando si esegue questo snippet ottengo il seguente errore.

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

Qualcuno può segnalare l'errore da quando sono molto nuovo a Rust?

Anche i suggerimenti su come scrivere questo snippet potrebbero essere accettati. Sono sempre alla ricerca di una leggibilità più facile/veloce.

+3

Perché pensi di aver bisogno di getter e setter? Perché non 'struct Person {pub first_name: String, pub last_name: String,}' che è molto più semplice? –

risposta

1

Il metodo Getter prende in prestito self. Quando restituisci self.name, stai spostando name da un riferimento preso a prestito che non è consentito. Dovresti restituire una copia del nome.

Inoltre, non è necessario passare un riferimento mutabile a self nei metodi getter poiché non si modifica la struttura interna.

Pertanto, i metodi getter dovrebbe essere:

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

Hey, grazie per l'utilissima risposta! Ha senso fare tutto direttamente, ora lo metti in quel modo. Ovviamente sono venuto da uno sfondo OOP molto pesante con PHP/Java. Quindi in questo caso ho bisogno di iniziare a pensare più semplicemente funzionale, in questo caso è più o meno quello che devo fare per la maggior parte. A meno che non stiamo parlando di aggiungere funzionalità alla struttura. – ajm113

10

Ok, il problema specifico qui non è essere in grado di uscire di contenuti preso in prestito. Questo è stato answerednumeroustimesbefore in una varietà di condizioni, per non parlare di the chapter on the subject of ownership in the Rust Book.

Il più interessante riguarda getter e setter. Sì, tu puoi scrivere in Rust, ma potrebbero non essere la scelta migliore.

Prima di andare avanti, voglio solo sottolineare che non v'è assolutamente alcuna ragione per richiedere&mut self su un getter ... a meno che non si ha intenzione di modificare il valore come parte di rimuovere il valore, ma allora sei non mi occupo più di un getter.

In secondo luogo, si non deveclone in un getter. Questo è estremamente dispendioso se tutti gli utenti vogliono fare, per esempio, leggere dal valore. È meglio restituire un prestito immutabile, dal quale l'utente può clonese è necessario il numero.

In ogni caso, se si stanno scrivendo questi perché si desidera eseguire una sorta di logica per convalidare nuovi valori, continuare a utilizzare i setter. In caso contrario, si potrebbe fare qualcosa di simile:

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

Questo dà agli utenti più o meno l'accesso diretto ai campi, senza realtà dando loro accesso diretto ai campi. Oltre a scrivere nuovi valori, questo consente anche agli utenti di modificare i valori esistenti sul posto, che possono essere importanti per cose grandi e allocate su heap.

Inoltre, ho fatto un paio di altri cambiamenti:

  • Si può solo derivare meccanicamente Default; in questo caso non c'è motivo di scriverlo da solo.

  • Lo stile convenzionale è snake_case per i campi.

  • Il modo in cui è stato creato il Person era inutilmente rotonda.

+0

Non è più corretto usare '& str' invece di' & String'? –

+0

@ W.K.S, divertente, in realtà ho avuto la stessa domanda. Ricordo di aver letto qualcosa sulla falsariga di usare str, ma non ricordo ... – ajm113

+0

@ ajm113 Normalmente sì. * Tuttavia *, in questo caso, * hai * anche accesso mutevole a 'String', e c'è una cosa che puoi fare con un' & String' che non puoi fare con un '& str': controlla la capacità. Inoltre, * in generale *, i due puntatori saranno dello stesso tipo. –