Ce face impl, self și Self
Un bloc impl Tip { ... } conține funcțiile care aparțin tipului. Cele care au primul parametru self sunt metode și se apelează cu punct: cont.depune(100). Cele fără self sunt funcții asociate și se apelează cu ::: Cont::new("Ana"). Prin convenție, constructorul se numește new.
Forma lui self spune ce face metoda cu valoarea: &self doar citește, &mut self modifică (iar variabila trebuie să fie mut), iar self ia valoarea (după apel, ea nu mai poate fi folosită – util pentru transformări ca into_...). La apel, Rust adaugă singur & sau &mut.
Self (cu majusculă) înseamnă „tipul pentru care scriu acest impl”, ca să nu repeți numele: fn new() -> Self, Self { ... }. impl Trait for Tip implementează un trait (de exemplu Display) pentru tip. Poți avea mai multe blocuri impl pentru același tip.
Exemple
Exemplul 1: Constructor, metode care citesc și metode care modifică
new returnează Self, adică ContBancar. depune și retrage primesc &mut self, deci cont trebuie declarat cu let mut. retrage returnează Result când suma nu este disponibilă.
struct ContBancar {
titular: String,
sold: u64,
}
impl ContBancar {
fn new(titular: &str) -> Self {
Self { titular: titular.to_string(), sold: 0 }
}
fn depune(&mut self, suma: u64) {
self.sold += suma;
}
fn retrage(&mut self, suma: u64) -> Result<(), String> {
if suma > self.sold {
return Err(format!("fonduri insuficiente: ai {} lei", self.sold));
}
self.sold -= suma;
Ok(())
}
fn raport(&self) -> String {
format!("{}: {} lei", self.titular, self.sold)
}
}
fn main() {
let mut cont = ContBancar::new("Ana Popescu");
cont.depune(500);
println!("{}", cont.raport());
if let Err(e) = cont.retrage(800) {
println!("Eroare: {e}");
}
cont.retrage(120).unwrap();
println!("{}", cont.raport());
}
Rezultat
Ana Popescu: 500 lei
Eroare: fonduri insuficiente: ai 500 lei
Ana Popescu: 380 lei
Exemplul 2: Metodă care consumă valoarea (self)
trimite primește self, deci după apel email nu mai poate fi folosit – ceea ce are sens, un e-mail se trimite o singură dată. cu_subiect folosește același stil pentru a construi pas cu pas valoarea.
struct Email {
catre: String,
subiect: String,
}
impl Email {
fn catre(adresa: &str) -> Self {
Self { catre: adresa.to_string(), subiect: String::new() }
}
fn cu_subiect(mut self, subiect: &str) -> Self {
self.subiect = subiect.to_string();
self
}
fn trimite(self) -> String {
format!("Trimis către {}: „{}”", self.catre, self.subiect)
}
}
fn main() {
let email = Email::catre("ana@exemplu.ro").cu_subiect("Factura din octombrie");
println!("{}", email.trimite());
}
Rezultat
Trimis către ana@exemplu.ro: „Factura din octombrie”
Exemplul 3: impl Trait for Tip: implementarea lui Display
După ce Temperatura implementează Display, poate fi afișată cu {} și primește automat metoda to_string().
use std::fmt;
struct Temperatura {
oras: String,
grade: f64,
}
impl fmt::Display for Temperatura {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}: {:.1}°C", self.oras, self.grade)
}
}
fn main() {
let t = Temperatura { oras: "Suceava".to_string(), grade: -3.4 };
println!("{t}");
let text = t.to_string();
println!("{} caractere", text.chars().count());
}
Rezultat
Suceava: -3.4°C
15 caractere
Greșeli frecvente
- Apelezi o funcție asociată cu punct (
cont.new()) sau o metodă cu::fără argumentulself. Funcțiile fărăselfse apeleazăTip::functie(). - Apelezi o metodă cu
&mut selfpe o variabilă declarată fărămut: eroareacannot borrow `cont` as mutable. - Scrii
self.campfărăself.în metodă. Spre deosebire de Java sau C++, câmpurile se accesează întotdeauna prinself. - Pui metode care doar citesc pe
selfîn loc de&self: după primul apel, valoarea nu mai poate fi folosită.
Comenzi înrudite
struct: Definește un tip nou care grupează mai multe valori cu nume (câmpuri), de exemplu un elev cu nume, clasă și medie.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.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.borrowing și referințe (& și &mut): O referință (&) împrumută o valoare fără să o ia de la proprietar. Regula: oricâte referințe de citire SAU una singură de modificare.