Rust este un limbaj de programare sistemică modern, conceput pentru a oferi siguranță memoriei și concurență fără necesitatea unui garbage collector. În practică, acest lucru se traduce prin aplicații extrem de rapide și stabile, ideale pentru infrastructură cloud, tooling de dezvoltare sau componente critice de sistem. Companiile din Silicon Valley și startup-urile europene îl adoptă tot mai des pentru a reduce bug-urile legate de gestionarea memoriei și pentru a îmbunătăți performanța. Dacă vrei să lucrezi în domeniu, stăpânirea Rustului nu este doar un avantaj tehnic, ci o dovadă că poți scrie cod robust și eficient.
Concepte cheie
- Ownership: Fiecare valoare din Rust are un singur proprietar, iar când proprietarul iese din scop, valoarea este distrusă automat, prevenind scurgerile de memorie.
- Borrowing: Permite funcțiilor să acceseze date fără a prelua proprietatea, folosind referințe imutabile (
&) sau mutabile (&mut), controlate strict de compilator. - Lifetimes: Anotări care asigură că referințele sunt valide atâta timp cât sunt folosite, prevenind accesul la memorie invalidată sau „dangling pointers”.
- Pattern Matching: O funcție puternică de control al fluxului care permite descompunerea structurilor de date complexe și tratarea cazurilor specifice, inclusiv a variantelor
enum. - Error Handling: Rust evită excepțiile, folosind tipul
Result<T, E>pentru erori recuperabile șipanic!pentru erori irecuperabile, forțând dezvoltatorul să gestioneze explicit situațiile de eroare. - Traits: Analogul interfețelor din alte limbaje, care definesc comportamente comune pe care le pot implementa diferite tipuri de date.
- Cargo: Sistemul de build și manager de pachete standardizat, similar cu
npmsaupip, esențial pentru gestionarea dependențelor și compilarea proiectelor.
Exemplu practic
Să presupunem că vrei să procesezi o listă de numere și să returnezi doar cele pare. În loc să modifici lista originală, folosești iter() pentru a crea o referință imutabilă.
fn filter_evens(numbers: &[i32]) -> Vec<i32> {
numbers.iter()
.filter(|&&x| x % 2 == 0)
.cloned()
.collect()
}
Aici, &[i32] indică faptul că funcția primește o referință la o secvență de întregi, fără a o prelua. filter aplică predicatul, iar collect() adună rezultatele într-un nou Vec. Compilatorul verifică la compilare că nu există acces concurrent periculos sau utilizare a memoriei eliberate.
Greșeli frecvente
- Ignorarea ownership-ului: Încercarea de a folosi o variabilă după ce a fost mutată în altă parte, fără a înțelege că valoarea a fost transferată.
- Confuzia între referințe imutabile și mutabile: Încercarea de a avea mai multe referințe mutabile simultan sau de a modifica date prin referință imutabilă.
- Abuzul de
unwrap(): Folosirea excesivă aunwrap()pentruResultsauOption, ceea ce duce la panici în runtime în loc de gestionare elegantă a erorilor. - Neînțelegerea lifetimes: Presupunerea că toate referințele au același ciclu de viață, ignorând anotările explicite necesare în structuri complexe.
Întrebări de interviu
De ce Rust este considerat sigur din punct de vedere al memoriei? Pentru că verifică la compilare regulile de ownership și borrowing, eliminând erorile comune precum use-after-free, double-free sau buffer overflows, fără a necesita un garbage collector.
Care este diferența dintre String și &str?
String este un tip deținut, alocat pe heap, care poate fi modificat. &str este o referință imutabilă la o secvență de caractere, adesea folosită pentru a evita copierea datelor.
Cum gestionezi erorile în Rust?
Folosind tipul Result<T, E> pentru erori așteptate și operatorul ? pentru propagarea lor simplă, sau match pentru tratarea detaliată a cazurilor specifice.
Exercițiu
Scrie o funcție care primește un HashMap<String, i32> reprezentând scorurile jucătorilor. Funcția trebuie să returneze un Vec<String> cu numele jucătorilor care au un scor strict mai mare decât 100, sortate alfabetic. Alocă memoria necesară și asigură-te că nu modifici HashMap-ul original. Timp estimat: 45 de minute.