Historia pytania
W Rust nie ma klasycznych konstruktorów, jak w językach OOP, ale są funkcje towarzyszące (najczęściej new), które implementują wzorzec konstruktorów z jawiczną inicjalizacją. Funkcje towarzyszące pozwalają na tworzenie instancji struktur z wstępnie ustawionymi wartościami lub na tworzenie ich z dodatkową logiką (na przykład, z rezerwacją pamięci).
Problem
Wielu programistów zaczyna używać tylko new, zapominając o innych wzorcach metod fabrycznych (na przykład with_capacity), lub błędnie realizuje inicjalizację struktur (szczególnie z prywatnymi/publicznymi polami), co może prowadzić do błędów inwariantów.
Rozwiązanie
Funkcja towarzysząca typu new jest implementowana przez impl, może być jedynym punktem wejścia do tworzenia obiektów z inwariantami. Funkcje takie jak with_capacity oferują dodatkowe możliwości, na przykład wcześniejsze alokowanie pamięci.
Przykład kodu:
pub struct MyBuffer { data: Vec<u8>, } impl MyBuffer { pub fn new() -> Self { MyBuffer { data: Vec::new() } } pub fn with_capacity(cap: usize) -> Self { MyBuffer { data: Vec::with_capacity(cap) } } }
Kluczowe cechy:
Czy jeśli nie zaimplementujesz new, Rust automatycznie go stworzy?
Nie. Nie ma automatycznego generowania new. Jeśli metoda nie jest jawnie zaimplementowana, nie będzie jej, nawet jeśli struktura ma tylko publiczne pola.
Jaka jest zasadnicza różnica między new a Default?
Default to konstruktor traitowy, który może być wywoływany tylko tam, gdzie typ implementuje lub jawnie dziedziczy Default. Jest standardowy dla algorytmów generycznych, ale nie zawsze pokrywa się z new.
Czy można używać metod new i with_capacity dla prywatnych struktur?
Tak, metody mogą być publiczne lub prywatne, ale stworzenie publicznego konstruktora dla prywatnej struktury uniemożliwia użytkownikom tworzenie instancji tej struktury poza modułem.
Otwarta struktura bez inwariantów, wszystkie pola - publiczne:
pub struct User { pub login: String, pub active: bool }
Zalety:
Wady:
Pola prywatne, tworzenie tylko przez new, inwarianty zapewnione:
pub struct User { login: String, active: bool } impl User { pub fn new(login: String) -> User { User { login, active: true } } }
Zalety:
Wady: