Rust nie ma specjalnych konstruktorów jak w C++/Java. Zwykle konstruktor jest realizowany jako funkcja powiązana (na przykład, new). Ważne cechy:
::.Result<Self, E> — co jest wygodne dla konstruktorów, które mogą zawodzić.with_capacity, from_str, niestandardowe warianty).Przykład podstawowego konstruktora:
struct Point { x: i32, y: i32 } impl Point { pub fn new(x: i32, y: i32) -> Self { Point { x, y } } }
Przykład konstruktora z możliwością wystąpienia błędu:
impl Point { pub fn try_new(x: i32, y: i32) -> Result<Self, String> { if x < 0 || y < 0 { Err("Współrzędne muszą być nieujemne".to_string()) } else { Ok(Point { x, y }) } } }
Jaki typ funkcji powinien być używany dla konstruktora: funkcja normalna, metoda czy funkcja powiązana, i dlaczego?
Wielu odpowiada: "metoda". Ale w Rust konstruktorzy to tylko funkcje powiązane (bez self), ponieważ instancja jeszcze nie istnieje i nie można wywołać zwykłej metody.
struct Foo; impl Foo { // funkcja powiązana, nie metoda pub fn new() -> Self { Foo } }
Historia
Programista zaimplementował konstruktor jako metodę:
impl Point { pub fn make(&self, x: i32, y: i32) -> Self { ... } }To jest nielogiczne i wprowadza w błąd: do stworzenia obiektu potrzebny jest obiekt, który jeszcze nie istnieje! Programista gubił się, jak wywołać tę metodę. Zmieniono na funkcję statyczną.
Historia
Klasyczny błąd: zwracanie Option<Self> dla konstruktora z możliwością wystąpienia błędu bez ostrzeżenia, co skutkuje pominięciem błędów kaskadowych. Po tym, jak zwrócono Result<Self, E>, stało się możliwe bardziej jawne informowanie o błędach.
Historia
Implementacja fabryk typu
from_parts,from_strbez użycia traitu From/FromStr. W rezultacie standardowe mechanizmy konwersji nie działały (na przykład,str.parse::<MyType>()). Po odkryciu błędu wprowadzono FromStr i zwiększono zgodność kodu z bibliotekami ekosystemu.