programowanieProgramista Rust / Programista Backend

Czym są konstruktory i metody fabryczne w Rust (na przykład, new, with_capacity), jak je prawidłowo zaimplementować i jakie błędy popełniają początkujący?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź.

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:

  • Funkcja powiązana nie jest związana z traitami ani obiektem, jest wywoływana przez ::.
  • Może zwracać zarówno self, jak i Result<Self, E> — co jest wygodne dla konstruktorów, które mogą zawodzić.
  • Można zaimplementować różne fabryki (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 }) } } }

Pytanie pułapka.

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 } }

Przykłady rzeczywistych błędów z powodu nieznajomości niuansów tematu.


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_str bez 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.