Wzorzec Newtype
Owinie Pan/Pani typy dla bezpieczeństwa i przejrzystości.
Wzorzec Newtype to bezpłatna lekcja Learn Rust Coding na CoddyKit. To lekcja 2 z 4. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Learn Rust Coding, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Learn Rust Coding zawiera 4 lekcji w sumie.
Czym jest newtype?
Newtype to krotka-struktura z jednym polem, która opakowuje istniejący typ, nadając mu odrębną tożsamość. struct Meters(f64) jest zupełnie nowym typem, mimo że przechowuje zwykłe f64.
Wrapper nie powoduje żadnego kosztu podczas działania programu, ale pozwala kompilatorowi egzekwować znaczenie, którego nie może zapewnić surowy typ prosty.
struct Meters(f64);
struct Seconds(f64);Zapobieganie pomyłkom jednostek
Surowe typy proste łatwo pomylić. Jeśli zarówno odległość, jak i czas mają typ f64, nic nie powstrzyma zamiany ich miejscami podczas wywołania.
Opakowanie każdej wartości w osobny newtype sprawia, że taki błąd zostanie wykryty podczas kompilacji, zamiast pozostać niezauważonym błędem.
fn speed(d: Meters, t: Seconds) -> f64 {
d.0 / t.0
}
// speed(Seconds(2.0), Meters(10.0)) -> compile errorDostęp do przechowywanej wartości
Do opakowanej wartości można dotrzeć za pomocą indeksu krotki .0. Wiele newtype'ów udostępnia również metodę albo implementuje From, aby ułatwić konwersje.
struct UserId(u64);
impl UserId {
fn value(&self) -> u64 { self.0 }
}
fn main() {
let id = UserId(42);
println!("id = {}", id.value());
}Hermetyzowanie niezmienników
Ustaw pole wewnętrzne jako prywatne i przeprowadzaj walidację w konstruktorze. Wtedy każda wartość newtype'u będzie gwarantowanie poprawna, więc kod korzystający z niej nie będzie musiał ponownie sprawdzać jej poprawności.
pub struct Email(String);
impl Email {
pub fn new(s: &str) -> Option<Email> {
if s.contains('@') {
Some(Email(s.to_string()))
} else {
None
}
}
}Reguła orphan
Rust zabrania implementowania obcej cechy dla obcego typu. Nie można napisać impl Display for Vec<T>, ponieważ nie jest się właścicielem ani cechy, ani typu.
Ta reguła zapewnia spójność implementacji cech między crate'ami, ale może uniemożliwić przydatne implementacje.
Newtype omija regułę orphan
Ponieważ newtype jest zdefiniowany w Państwa crate, mają Państwo teraz lokalny typ i mogą zaimplementować dla niego dowolny trait. To standardowy sposób obejścia reguły orphan.
use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}Runnable Wrapper Display
W tym miejscu używany jest wrapper z poprzedniej sceny. Należy do nas Wrapper, więc można dla niego zaimplementować Display, a program wypisuje połączoną listę.
use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
fn main() {
let w = Wrapper(vec!["a".into(), "b".into()]);
println!("{}", w);
}Ograniczanie powierzchni API
Opakowanie rozbudowanego typu pozwala udostępnić tylko bezpieczny podzbiór jego możliwości. NonEmptyVec może ukrywać metody modyfikujące, które pozwoliłyby mu stać się pustym, zachowując w ten sposób swój niezmiennik.
pub struct NonEmptyVec<T>(Vec<T>);
impl<T> NonEmptyVec<T> {
pub fn new(first: T) -> Self {
NonEmptyVec(vec![first])
}
pub fn first(&self) -> &T {
&self.0[0]
}
}Abstrakcja bez kosztu
Newtype z jednym polem ma taki sam układ pamięci jak opakowywana wartość. Kompilator optymalizuje wrapper, więc bezpieczeństwo nie wiąże się tutaj z żadnym kosztem w czasie działania programu.
Dodanie #[repr(transparent)] gwarantuje identyczny układ pamięci, co ma znaczenie w przypadku FFI.
#[repr(transparent)]
struct Celsius(f64);Automatyczne implementowanie traitów dla newtype
Newtype często automatycznie implementuje standardowe traity, aby tam, gdzie jest to właściwe, zachowywać się jak wartość wewnętrzna. Automatyczne implementowanie ułatwia używanie ich jako kluczy, podczas porównań i przy wyświetlaniu informacji debugowania.
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
struct ProductId(u32);
fn main() {
let a = ProductId(7);
let b = a.clone();
println!("{:?} == {:?}: {}", a, b, a == b);
}Newtype a alias typu
Nie należy mylić newtype z aliasem typu. type Meters = f64 to tylko nazwa; nadal jest to f64 i nie zapewnia żadnego dodatkowego bezpieczeństwa.
Newtype struct Meters(f64) jest rzeczywiście odrębnym typem, który kompilator może traktować niezależnie.
type Km = f64; // alias: interchangeable with f64
struct Mi(f64); // newtype: distinct from f64Szybkie sprawdzenie
Proszę ustalić, dlaczego newtype pomaga w sytuacji, w której alias typu nie zapewnia takiej korzyści.
Podsumowanie
Wzorzec newtype opakowuje istniejący typ w krotkową strukturę z jednym polem, aby uzyskać odrębną tożsamość bez kosztu w czasie działania programu. Zapobiega pomyleniu wartości, ukrywa niezmienniki za prywatnym polem i omija regułę orphan, dzięki czemu można implementować obce traity.
W przeciwieństwie do aliasu typu newtype jest rzeczywistym, odrębnym typem.
Często zadawane pytania
Czy lekcja „Wzorzec Newtype” jest bezpłatna?
Tak — pełny tekst „Wzorzec Newtype” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Learn Rust Coding, przejdź na CoddyKit PRO. Kurs Learn Rust Coding zawiera 4 lekcji w sumie.
Co nauczysz się w „Wzorzec Newtype”?
Owinie Pan/Pani typy dla bezpieczeństwa i przejrzystości. Ćwiczysz Learn Rust Coding z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.
Czy potrzebuję doświadczenia, aby zacząć Learn Rust Coding?
Nie wymagamy żadnego doświadczenia. Learn Rust Coding w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 4.
Ile czasu zajmuje lekcja „Wzorzec Newtype”?
Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.
Czy mogę pisać i uruchamiać kod w tej lekcji Learn Rust Coding?
Tak. Każda lekcja Learn Rust Coding zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.