Ce face trait
Un trait descrie un comportament: o listă de metode (semnături), eventual cu implementări implicite. Un tip „are” trait-ul după ce scrii impl Trait for Tip { ... } și dai implementările care lipsesc. Același trait poate fi implementat de oricâte tipuri.
Trait-urile sunt folosite ca limite în funcții generice: fn afiseaza(x: &impl Descriere) sau fn afiseaza<T: Descriere>(x: &T) acceptă orice tip care implementează Descriere. Pentru o colecție de tipuri diferite cu același trait se folosesc trait objects (Box<dyn Trait>).
Biblioteca standard are multe trait-uri pe care le implementezi des: Display (afișare cu {}), Debug, Clone, PartialEq, Default, From, Iterator. Multe se pot genera automat cu #[derive(...)]. Ca să folosești metodele unui trait, trait-ul trebuie să fie în domeniu (importat cu use), dacă nu este în preludiu.
Exemple
Exemplul 1: Un trait cu o metodă implicită
Forma cere arie și nume; descriere are implementare implicită, folosită de ambele tipuri. Funcția raport acceptă orice tip care implementează Forma.
trait Forma {
fn arie(&self) -> f64;
fn nume(&self) -> String;
fn descriere(&self) -> String {
format!("{} cu aria {:.2}", self.nume(), self.arie())
}
}
struct Cerc {
raza: f64,
}
struct Dreptunghi {
latime: f64,
inaltime: f64,
}
impl Forma for Cerc {
fn arie(&self) -> f64 {
std::f64::consts::PI * self.raza * self.raza
}
fn nume(&self) -> String {
"cerc".to_string()
}
}
impl Forma for Dreptunghi {
fn arie(&self) -> f64 {
self.latime * self.inaltime
}
fn nume(&self) -> String {
format!("dreptunghi {}x{}", self.latime, self.inaltime)
}
}
fn raport(f: &impl Forma) {
println!("- {}", f.descriere());
}
fn main() {
raport(&Cerc { raza: 2.0 });
raport(&Dreptunghi { latime: 3.0, inaltime: 4.5 });
}
Rezultat
- cerc cu aria 12.57
- dreptunghi 3x4.5 cu aria 13.50
Exemplul 2: Implementarea unui trait standard: From
După impl From<u32> for Bani, Rust oferă automat și into() în sens invers. #[derive(Debug, PartialEq)] generează alte două trait-uri fără cod scris de mână.
#[derive(Debug, PartialEq)]
struct Bani {
lei: u32,
bani: u32,
}
impl From<u32> for Bani {
fn from(total_bani: u32) -> Self {
Bani { lei: total_bani / 100, bani: total_bani % 100 }
}
}
fn main() {
let pret = Bani::from(1_299);
let rest: Bani = 450.into();
println!("{pret:?}");
println!("{rest:?}");
println!("{}", rest == Bani { lei: 4, bani: 50 });
}
Rezultat
Bani { lei: 12, bani: 99 }
Bani { lei: 4, bani: 50 }
true
Exemplul 3: Trait pentru un tip din biblioteca standard
Poți implementa propriul trait și pentru tipuri existente, de exemplu str. Regula (orphan rule): trait-ul sau tipul trebuie să fie definit în crate-ul tău.
trait Initiale {
fn initiale(&self) -> String;
}
impl Initiale for str {
fn initiale(&self) -> String {
self.split_whitespace()
.filter_map(|cuvant| cuvant.chars().next())
.map(|c| format!("{c}."))
.collect()
}
}
fn main() {
println!("{}", "Mihai Eminescu".initiale());
println!("{}", "Ion Luca Caragiale".initiale());
}
Rezultat
M.E.
I.L.C.
Greșeli frecvente
- Apelezi metoda unui trait care nu este importat:
no method named `...` found, iar compilatorul îți sugereazăuse-ul necesar. - Încerci să implementezi un trait străin pentru un tip străin (
impl Display for Vec<i32>): eroarea E0117 (orphan rule). Împachetează tipul într-o structură proprie (struct Lista(Vec<i32>);). - Uiți una dintre metodele obligatorii: E0046
not all trait items implemented, cu lista metodelor lipsă.
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.generics și where: Scrii o funcție sau un tip o singură dată, pentru orice tip T; limitele (T: Trait) spun ce trebuie să știe să facă T.trait objects (dyn Trait): dyn Trait înseamnă „o valoare de orice tip care implementează trait-ul”, aleasă la rulare. Permite colecții cu tipuri diferite.impl Trait: Scrie „un tip care implementează Trait” fără să-l numești: ca parametru este o scurtătură pentru generics, ca rezultat ascunde tipul exact.atribute (#[derive], #[cfg], #[allow]): Informații pentru compilator scrise cu #[…] deasupra unui element: generează cod (derive), aleg codul compilat (cfg) sau schimbă avertismentele.