Spesso è utile raggruppare una raccolta di elementi e gestire questi gruppi come unità. In Rust, un gruppo così si chiama struct, e ogni elemento è uno dei campi della struct. Una struct definisce l'insieme generale dei campi disponibili, ma un particolare esempio di una struct si chiama istanza.
Inoltre, le struct possono avere dei metodi definiti su di esse, che hanno accesso ai campi. In questo caso la struct stessa viene chiamata self. Quando un metodo usa &mut self, i campi possono essere modificati, cioè mutati. Quando un metodo usa &self, i campi non possono essere cambiati: sono immutabili. Controllare la mutabilità aiuta il borrow-checker a garantire che intere classi di bug di concorrenza semplicemente non si verifichino in Rust.
In questo esercizio implementerai due tipi di metodi su una struct. I primi sono generalmente noti come getter: espongono i campi della struct al mondo esterno, senza permettere a nessun altro di modificare quel valore. In Rust, questi metodi per convenzione portano lo stesso nome del campo che espongono, cioè, se abbiamo un metodo getter che recupera un campo della struct chiamato name, il metodo si chiamerà semplicemente name().
Implementerai anche metodi di un altro tipo, generalmente noti come setter. Questi cambiano il valore del campo. I setter non sono molto comuni in Rust: se un campo può essere modificato liberamente, è più comune renderlo semplicemente pubblico. Sono però utili se aggiornare il campo dovrebbe avere effetti collaterali, o per il controllo degli accessi: un setter contrassegnato come pub(crate) permette ad altri moduli dello stesso crate di aggiornare un campo privato, che non può essere modificato dall'esterno.
Le struct esistono in tre varianti: struct con campi nominati, tuple struct e unit struct. In questo esercizio di concetto esploreremo la prima variante: le struct con campi nominati.
Le struct si definiscono usando la parola chiave struct, seguita dal nome con l'iniziale maiuscola del tipo che la struct descrive:
struct Item {}
Poi si introducono altri tipi nel corpo della struct come campi della struct stessa, ognuno con il proprio tipo:
struct Item {
name: String,
weight: f32,
worth: u32,
}
Infine, si possono definire metodi sulle struct all'interno di un blocco impl:
impl Item {
// initializes and returns a new instance of our Item struct
fn new() -> Self {
todo!()
}
}
Con questa breve introduzione alla sintassi delle struct, dai un'occhiata alle istruzioni per questo esercizio!
Stai lavorando all'implementazione di un sistema di monitoraggio della salute. Come parte di questo, devi tenere traccia delle statistiche di salute degli utenti.
Inizierai con alcune funzioni abbozzate in un blocco impl e con la seguente definizione di struct:
pub struct User {
name: String,
age: u32,
weight: f32,
}
Il tuo obiettivo è implementare i metodi abbozzati sullo struct User definito nel blocco impl.
Per esempio, il metodo new dovrebbe restituire un'istanza dello struct User con i valori specificati di nome, età e peso.
let mut bob = User::new(String::from("Bob"), 32, 155.2);
// Returns: a User with name "Bob", age 32, and weight 155.2
Il metodo weight dovrebbe restituire il peso dello User.
bob.weight();
// Returns: 155.2
Il metodo set_age dovrebbe impostare l'età dello User.
bob.set_age(33);
// Updates Bob's age to 33; happy birthday Bob!
Divertiti!
Iscriviti a Exercism per imparare e padroneggiare Rust con 99 esercizi e il mentoring di persone reali, tutto gratis.