struct în Rust

Definește un tip nou care grupează mai multe valori cu nume (câmpuri), de exemplu un elev cu nume, clasă și medie.

Sintaxă
struct Nume {
    camp1: Tip1,
    camp2: Tip2,
}
struct Punct(i32, i32);     // tuple struct
struct Marcaj;              // unit struct
let v = Nume { camp1: a, camp2: b };
let w = Nume { camp1: c, ..v };   // restul câmpurilor din v

Ce face struct

struct creează un tip propriu cu câmpuri numite. O valoare se construiește dând toate câmpurile: Elev { nume: ..., clasa: 10 }, iar câmpurile se citesc cu punct: elev.nume. Ca să modifici un câmp, toată variabila trebuie să fie mut; Rust nu are câmpuri mutabile individual.

Scurtături utile: dacă o variabilă are același nume ca un câmp, scrii doar Elev { nume, clasa }. Sintaxa ..alt ia restul câmpurilor dintr-o altă valoare. Metodele se adaugă separat, într-un bloc impl.

Există și tuple struct (struct Lei(f64);), cu câmpuri fără nume accesate cu .0, utile ca să faci tipuri distincte pentru aceeași reprezentare (lei vs. euro), și unit struct (struct Gol;), fără câmpuri. Afișarea cu {:?} cere #[derive(Debug)] deasupra structurii.

Exemple

Exemplul 1: Definire, creare și modificare

#[derive(Debug)] permite afișarea cu {:?} și {:#?}. Câmpul media se poate modifica doar pentru că ana este let mut. Elev { nume, ..ana.clone() } creează un elev nou cu alt nume și restul datelor copiate.

#[derive(Debug, Clone)]
struct Elev {
    nume: String,
    clasa: u8,
    media: f64,
}

fn main() {
    let mut ana = Elev { nume: String::from("Ana"), clasa: 10, media: 9.2 };
    ana.media = 9.4;
    println!("{} din clasa a {}-a are media {}", ana.nume, ana.clasa, ana.media);

    let nume = String::from("Mihai");
    let mihai = Elev { nume, ..ana.clone() };
    println!("{mihai:?}");
    println!("{ana:#?}");
}

Rezultat

Ana din clasa a 10-a are media 9.4
Elev { nume: "Mihai", clasa: 10, media: 9.4 }
Elev {
    nume: "Ana",
    clasa: 10,
    media: 9.4,
}

Exemplul 2: Tuple struct și unit struct

Lei și Euro conțin amândouă un f64, dar sunt tipuri diferite: compilatorul nu te lasă să aduni lei cu euro din greșeală. Gratuit nu are date; contează doar tipul lui.

#[derive(Debug, Clone, Copy)]
struct Lei(f64);

struct Euro(f64);

struct Gratuit;

fn in_euro(suma: Lei) -> Euro {
    Euro(suma.0 / 4.97)
}

fn cost_livrare(_: Gratuit) -> Lei {
    Lei(0.0)
}

fn main() {
    let pret = Lei(248.5);
    println!("{:?} = {:.2} euro", pret, in_euro(pret).0);
    println!("Livrare: {:?}", cost_livrare(Gratuit));
}

Rezultat

Lei(248.5) = 50.00 euro
Livrare: Lei(0.0)

Greșeli frecvente

  • Încerci să faci un singur câmp mutabil (mut media: f64 în definiție). Nu există; mutabilitatea ține de variabilă: let mut elev = ....
  • Afișezi o structură cu {:?} fără #[derive(Debug)]: eroarea `Elev` doesn't implement `Debug`. Pentru {} trebuie să implementezi singur Display.
  • Folosești ..alt cu o structură care are câmpuri String și apoi folosești alt: câmpurile luate de acolo au fost mutate. Clonează (..alt.clone()) sau dă toate câmpurile cu String.

Comenzi înrudite

  • impl, self și Self: impl adaugă metode și funcții asociate unui tip; self este valoarea pe care se apelează metoda, iar Self este tipul însuși.
  • enum: Definește un tip care poate fi exact una dintre mai multe variante; fiecare variantă poate avea propriile date.
  • trait: Definește un set de metode pe care un tip promite să le aibă. Seamănă cu interfețele din alte limbaje și stă la baza generics.
  • atribute (#[derive], #[cfg], #[allow]): Informații pentru compilator scrise cu #[…] deasupra unui element: generează cod (derive), aleg codul compilat (cfg) sau schimbă avertismentele.
  • pattern matching (tipare): Tiparele descriu forma unei valori și scot din ea părțile de care ai nevoie: în let, match, if let, for și parametrii funcțiilor.