Metody generyczne
Część sekcji Programowanie obiektowe ścieżki Rust w Coddy. Lekcja 25 z 61.
Struktura generyczna może przechowywać dowolny typ, ale bez metod umożliwiających interakcję z jej danymi nie jest zbyt użyteczna. Aby zdefiniować metody dla struktury generycznej, potrzebujesz specjalnej składni w bloku impl.
Kluczowe jest zadeklarowanie parametru generycznego przy samym impl:
struct Wrapper<T> {
value: T,
}
impl<T> Wrapper<T> {
fn get(&self) -> &T {
&self.value
}
}
Zwróć uwagę na impl<T> przed Wrapper<T>. Informuje to Rust, że T jest parametrem typu generycznego dla całego bloku implementacji. Bez tej deklaracji Rust szukałby konkretnego typu o nazwie T i nie zdołałby go znaleźć.
Metoda get zwraca &T: referencję do dowolnego typu przechowywanego przez opakowanie. Działa to niezależnie od tego, czy T jest liczbą całkowitą, ciągiem znaków czy dowolnym innym typem:
let num_wrapper = Wrapper { value: 100 };
let text_wrapper = Wrapper { value: "Rust" };
println!("{}", num_wrapper.get()); // 100
println!("{}", text_wrapper.get()); // Rust
Ta sama definicja metody działa w obu przypadkach, ponieważ typ generyczny T dostosowuje się do każdego konkretnego typu podczas kompilacji.
Wyzwanie
ŁatwyRozszerzmy twój generyczny kontener o metody! Zbudujesz strukturę Box (nie myl jej ze standardową strukturą Box w Rust), która może przechowywać dowolny typ i udostępnia metody pozwalające korzystać z jej zawartości.
Podzielisz swój kod na dwa pliki:
mybox.rs: Zdefiniuj publiczną strukturę generyczną o nazwieMyBox<T>z prywatnym polemcontentstypuT. Zaimplementuj metody dla tej struktury:- Funkcję powiązaną
new, która tworzy nową wartośćMyBoxz podaną wartością - Metodę
peek, która zwraca referencję do zawartości (używając&self) - Metodę
replace, która przyjmuje nową wartość i zastępuje nią obecną zawartość (używając&mut self)
- Funkcję powiązaną
main.rs: Dołącz swój moduł i pokaż działanie metod generycznych na różnych typach. Utworzysz pudełka, sprawdzisz ich zawartość i zastąpisz wartości, aby pokazać działanie metod.
Pamiętaj o kluczowej składni implementowania metod dla struktury generycznej: przed MyBox<T> musisz umieścić impl<T>, aby poinformować Rust, że T jest parametrem generycznym dla całego bloku implementacji.
W pliku głównym pokaż działanie MyBox, wykonując następujące czynności:
- Utwórz pudełko z liczbą całkowitą (pierwsze wejście, sparsowane jako
i32) - Sprawdź jego zawartość i wypisz wartość
- Zastąp zawartość nową liczbą całkowitą (drugie wejście, sparsowane jako
i32) - Ponownie sprawdź zawartość, aby pokazać zaktualizowaną wartość
- Utwórz drugie pudełko z ciągiem znaków (trzecie wejście)
- Sprawdź zawartość pudełka z ciągiem znaków
Wynik powinien mieć następujący format:
Integer box contains: {value}
After replace: {value}
String box contains: {value}Na przykład dla wejść 10, 25 i Rust:
Integer box contains: 10
After replace: 25
String box contains: RustOtrzymasz trzy wejścia: początkową liczbę całkowitą, liczbę całkowitą do zastąpienia oraz ciąg znaków.
Spróbuj swoich sił
mod mybox;
use mybox::MyBox;
fn main() {
// Wczytaj dane wejściowe
let mut input1 = String::new();
std::io::stdin().read_line(&mut input1).expect("Failed to read line");
let initial_int: i32 = input1.trim().parse().expect("Invalid integer");
let mut input2 = String::new();
std::io::stdin().read_line(&mut input2).expect("Failed to read line");
let replacement_int: i32 = input2.trim().parse().expect("Invalid integer");
let mut input3 = String::new();
std::io::stdin().read_line(&mut input3).expect("Failed to read line");
let string_value = input3.trim().to_string();
// TODO: Utwórz MyBox z początkową liczbą całkowitą
// TODO: Sprawdź jego zawartość i wypisz: "Integer box contains: {value}"
// TODO: Zastąp zawartość nową liczbą całkowitą
// TODO: Ponownie sprawdź zawartość i wypisz: "After replace: {value}"
// TODO: Utwórz drugi MyBox z wartością tekstową
// TODO: Sprawdź zawartość pudełka z tekstem i wypisz: "String box contains: {value}"
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Metody i zachowanie
Wprowadzenie do bloków implementacjiParametr selfMetody modyfikująceFunkcje powiązaneWiele bloków implementacjiŁańcuchowe wywoływanie metodPowtórzenie — działania na prostokącie4Projekt: Wirtualne zwierzątko
Definiowanie zwierzątkaKarmienie zwierzątka7Standardowe cechy
Cecha DebugCecha DisplayClone i CopyCechy równościPowtórka – punkt do wydrukowania10Projekt: System dokumentów
Trait DrawKomponent tekstowy2Hermetyzacja i moduły
Podstawy modułówSłowo kluczowe publicPola prywatneGetterySetteryPodsumowanie — bezpieczna skrytka5Typy generyczne
Struktury generyczneMetody generyczneWiele typów generycznychFunkcje generycznePodsumowanie – punkt współrzędnych8Cechy jako ograniczenia
Składnia ograniczeń cechWiele ograniczeńKlauzula whereZwracanie typów z użyciem cechPodsumowanie — generyczna drukarka11Wzorce projektowe w Rust
Wzorzec NewtypeKompozycjaTrait DropFrom i IntoPodsumowanie — atrapowy inteligentny wskaźnik3Zaawansowane typy wyliczeniowe
Typy wyliczeniowe z danymiMetody typów wyliczeniowychDopasowywanie wariantów danychTyp wyliczeniowy Option — powtórzeniePodsumowanie — typ wyliczeniowy Shape6Definiowanie cech
Czym jest cecha?Implementowanie cechImplementacje domyślneNadpisywanie wartości domyślnychCechy z parametramiPowtórzenie — odtwarzacz multimedialnyPoćwicz samodzielnie: Kompilator Rust online