Default în Rust

Trait-ul pentru valoarea implicită a unui tip: Tip::default(), derive(Default) și ..Default::default().

Sintaxă
#[derive(Default)]
struct Tip { ... }

pub trait Default: Sized {
    fn default() -> Self;
}

Ce face Default

Default::default() creează o valoare „de pornire”: 0 pentru numere, false pentru bool, text gol pentru String, vector gol pentru Vec, None pentru Option. Cu #[derive(Default)], o structură primește valoarea implicită a fiecărui câmp; un enum își alege varianta implicită cu atributul #[default].

Când valorile implicite trebuie să fie altele (limba „ro”, 10 rezultate pe pagină), implementezi manual impl Default. Combinat cu sintaxa ..Default::default(), poți scrie doar câmpurile care diferă. Mai multe funcții din std se bazează pe Default: unwrap_or_default, mem::take, entry().or_default().

Ce returnează

default() returnează o valoare nouă a tipului.

Exemple

Exemplul 1: derive(Default) și #[default] pentru enum

#[derive(Debug, Default)]
enum Tema {
    #[default]
    Luminoasa,
    Intunecata,
}

#[derive(Debug, Default)]
struct Profil {
    nume: String,
    varsta: u32,
    abonat: bool,
    oras: Option<String>,
    tema: Tema,
}

fn main() {
    let p = Profil::default();
    println!("{p:?}");
    println!("nume gol: {}, vârstă: {}, abonat: {}, oraș: {:?}", p.nume.is_empty(), p.varsta, p.abonat, p.oras);
    let t = Tema::Intunecata;
    println!("{:?} / {t:?}, implicit: {:?}", p.tema, Tema::default());
}

Rezultat

Profil { nume: "", varsta: 0, abonat: false, oras: None, tema: Luminoasa }
nume gol: true, vârstă: 0, abonat: false, oraș: None
Luminoasa / Intunecata, implicit: Luminoasa

Exemplul 2: Implementare manuală și ..Default::default()

struct Setari {
    limba: String,
    pe_pagina: u32,
    notificari: bool,
}

impl Default for Setari {
    fn default() -> Self {
        Setari { limba: String::from("ro"), pe_pagina: 10, notificari: true }
    }
}

fn main() {
    let implicite = Setari::default();
    let ale_mele = Setari { pe_pagina: 50, ..Default::default() };
    for (nume, s) in [("implicite", &implicite), ("ale mele", &ale_mele)] {
        println!("{nume}: limba={}, pe_pagina={}, notificari={}", s.limba, s.pe_pagina, s.notificari);
    }
    let gol: Option<u32> = None;
    println!("unwrap_or_default: {}", gol.unwrap_or_default());
}

Rezultat

implicite: limba=ro, pe_pagina=10, notificari=true
ale mele: limba=ro, pe_pagina=50, notificari=true
unwrap_or_default: 0

Greșeli frecvente

  • Pui #[derive(Default)] pe un enum fără #[default] pe o variantă: `#[derive(Default)]` on enum with no `#[default]`.
  • Definești o funcție new() fără argumente și uiți de Default. Clippy recomandă să le ai pe amândouă (new_without_default).

Comenzi înrudite

  • Clone: Trait-ul pentru copierea explicită a unei valori cu .clone(); se obține cu #[derive(Clone)] sau implementare manuală.
  • fmt::Debug: Trait-ul pentru afișarea de depanare cu {:?} și {:#?}; se obține aproape mereu cu #[derive(Debug)].
  • From: Trait-ul pentru conversii care nu pot eșua: Tinta::from(x). Implementându-l, primești gratuit și x.into().