Ce face atribute (#[derive], #[cfg], #[allow])
Un atribut #[...] se aplică elementului de sub el (funcție, structură, câmp, modul, instrucțiune). Forma #![...], pusă la începutul unui fișier sau modul, se aplică întregului conținut. Cele mai folosite:
#[derive(...)] generează automat implementări de trait-uri: Debug (afișare cu {:?}), Clone, Copy, PartialEq/Eq (comparare cu ==), PartialOrd/Ord, Hash (chei în HashMap), Default. #[cfg(...)] compilează elementul doar dacă o condiție este adevărată: sistemul de operare (target_os = "windows"), modul test (test), modul debug (debug_assertions) sau o funcționalitate Cargo (feature = "x"). Macro-ul cfg! face aceeași verificare, dar ca valoare bool.
Atributele de lint schimbă avertismentele: #[allow(dead_code)] le ascunde, #[expect(dead_code)] le ascunde, dar avertizează dacă nu mai apar, #[deny(...)] le face erori. #[must_use] avertizează când rezultatul unei funcții este ignorat, #[test] marchează un test, #[inline] sugerează compilatorului să insereze funcția în locul apelului.
Exemple
Exemplul 1: derive: trait-uri generate automat
Fără nicio linie scrisă de mână, Nota poate fi afișată, clonată, comparată, sortată și folosită ca cheie. Default dă valori zero/goale. Compararea generată de derive se face câmp cu câmp, în ordinea declarării: întâi materie („Fizică” < „Mate”), apoi valoare; de aceea a < c este fals.
use std::collections::HashSet;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
struct Nota {
materie: String,
valoare: u8,
}
fn main() {
let a = Nota { materie: "Mate".to_string(), valoare: 9 };
let b = a.clone();
let c = Nota { materie: "Fizică".to_string(), valoare: 10 };
println!("{a:?}");
println!("a == b: {}, a < c: {}", a == b, a < c);
let mut note = vec![a, c, b];
note.sort();
let unice: HashSet<Nota> = note.iter().cloned().collect();
println!("sortate: {:?}", note.iter().map(|n| n.valoare).collect::<Vec<_>>());
println!("unice: {}", unice.len());
println!("implicit: {:?}", Nota::default());
}
Rezultat
Nota { materie: "Mate", valoare: 9 }
a == b: true, a < c: false
sortate: [10, 9, 9]
unice: 2
implicit: Nota { materie: "", valoare: 0 }
Exemplul 2: cfg: cod diferit pe sisteme diferite
Se compilează doar una dintre cele trei variante ale lui separator_cale. Exemplul a fost compilat și rulat pe macOS, care este un sistem Unix. debug_assertions este activ la compilarea fără optimizări (cargo build), dar nu și la cargo build --release.
#[cfg(windows)]
fn separator_cale() -> char {
'\\'
}
#[cfg(unix)]
fn separator_cale() -> char {
'/'
}
#[cfg(not(any(unix, windows)))]
fn separator_cale() -> char {
'/'
}
fn main() {
println!("Separatorul de cale: {}", separator_cale());
#[cfg(debug_assertions)]
println!("Compilat în modul debug");
#[cfg(not(debug_assertions))]
println!("Compilat în modul release");
}
Rezultat
Separatorul de cale: /
Compilat în modul debug
Exemplul 3: allow, expect și must_use
Fără #[allow(dead_code)], funcția nefolosită pentru_mai_tarziu ar produce un avertisment (aici, eroare). #[must_use] pe aplica_reducere avertizează dacă cineva o apelează și ignoră rezultatul.
#[allow(dead_code)]
fn pentru_mai_tarziu() {}
#[must_use = "funcția nu modifică prețul, returnează unul nou"]
fn aplica_reducere(pret: f64, procent: f64) -> f64 {
pret * (1.0 - procent / 100.0)
}
fn main() {
#[expect(unused_variables)]
let neterminat = 42;
let pret = aplica_reducere(250.0, 20.0);
println!("Preț redus: {pret} lei");
}
Rezultat
Preț redus: 200 lei
Greșeli frecvente
- Faci
#[derive(PartialEq)]pe o structură care conține un câmp fărăPartialEq: derive cere ca toate câmpurile să implementeze trait-ul. - Folosești
#[derive(Copy)]fărăClonesau pe o structură cuString:CopycereCloneși doar câmpuriCopy. - Pui
#![allow(warnings)]la începutul proiectului ca să scapi de avertismente. Ascunzi astfel și probleme reale; dezactivează doar avertismentul exact, doar unde este nevoie, de preferat cu#[expect(...)].
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.teste (#[test]): O funcție marcată cu #[test] este un test automat: cargo test o rulează și raportează dacă a trecut sau a dat panică.cfg!: Evaluează la compilare o condiție de configurare (sistem de operare, mod debug, feature Cargo) și o transformă în true sau false.comentarii și documentație (//, ///, //!): // și /* */ sunt comentarii obișnuite; /// și //! scriu documentație Markdown, transformată în HTML de cargo doc și testată de cargo test.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.