Ce face impl Trait
Ca tip de parametru, impl Trait este o scurtătură pentru generics: fn afiseaza(x: impl Display) înseamnă același lucru ca fn afiseaza<T: Display>(x: T). Este util pentru funcții simple, cu un singur parametru generic.
Ca tip de rezultat, impl Trait spune „returnez un singur tip concret, dar nu îți spun care”. Este indispensabil pentru tipuri care nu pot fi scrise de mână: closures (impl Fn(i32) -> i32) și lanțuri de iteratori (impl Iterator<Item = u32>), al căror tip real este foarte lung.
Diferența față de Box<dyn Trait>: impl Trait este rezolvat la compilare, fără alocare și fără vtable, dar funcția trebuie să returneze același tip pe toate ramurile. Dacă ramurile returnează tipuri diferite, ai nevoie de Box<dyn Trait>.
Exemple
Exemplul 1: Returnarea unui iterator
Tipul real al rezultatului este ceva de felul Filter<Map<Range<u32>, ...>, ...>, imposibil de scris. impl Iterator<Item = u32> spune doar ce contează: produce numere u32.
fn ani_bisecti(de_la: u32, pana_la: u32) -> impl Iterator<Item = u32> {
(de_la..=pana_la).filter(|an| (an % 4 == 0 && an % 100 != 0) || an % 400 == 0)
}
fn main() {
let lista: Vec<u32> = ani_bisecti(1996, 2030).collect();
println!("{lista:?}");
println!("Între 1900 și 2100: {}", ani_bisecti(1900, 2100).count());
}
Rezultat
[1996, 2000, 2004, 2008, 2012, 2016, 2020, 2024, 2028]
Între 1900 și 2100: 49
Exemplul 2: Returnarea unui closure și parametru impl Trait
calculator_tva returnează un closure care ține minte procentul. afiseaza_toate primește orice colecție care se poate parcurge și ale cărei elemente se pot afișa.
use std::fmt::Display;
fn calculator_tva(procent: f64) -> impl Fn(f64) -> f64 {
move |pret| pret * (1.0 + procent / 100.0)
}
fn afiseaza_toate(elemente: impl IntoIterator<Item = impl Display>) {
for e in elemente {
print!("[{e}] ");
}
println!();
}
fn main() {
let tva_standard = calculator_tva(21.0);
let tva_redus = calculator_tva(11.0);
println!("{:.2} / {:.2}", tva_standard(100.0), tva_redus(100.0));
afiseaza_toate(vec!["Iași", "Vaslui"]);
afiseaza_toate([1.5, 2.25]);
}
Rezultat
121.00 / 111.00
[Iași] [Vaslui]
[1.5] [2.25]
Greșeli frecvente
- Returnezi tipuri diferite pe ramuri diferite (
if a { Cerc } else { Patrat }) cu-> impl Forma:`if` and `else` have incompatible types. FoloseșteBox<dyn Forma>sau unenum. - Folosești
impl Traitîn parametri când apelantul are nevoie să aleagă tipul explicit (f::<u8>(...)); cuimpl Traitasta nu este posibil, deci folosește<T: Trait>. - Te aștepți ca apelantul să poată folosi metode ale tipului concret. Cu
-> impl Iterator, apelantul vede doar metodele luiIterator.
Comenzi înrudite
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.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: 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.closures și move: Funcții anonime, scrise cu |parametri|, care pot folosi variabilele din jur. move le dă proprietatea asupra acelor variabile.type (alias de tip și tip asociat): Dă un nume nou unui tip existent (alias), ca să scurtezi tipuri lungi; în trait-uri, declară un tip asociat.