Poznanie tych dwóch koncepcji pomoże ci lepiej zrozumieć, jak działa Rust i jak możesz zaimplementować funkcje OOP.

Cechy i okresy życia są kluczowymi składnikami Rusta. Możesz użyć cech, aby zdefiniować zachowania i możliwości dla typów do zaimplementowania. Są bardzo wszechstronne, pozwalają pisać bardziej ogólny kod, ograniczają powielanie i poprawiają łatwość konserwacji.

Rust używa innego mechanizmu — czasów życia — do śledzenia własności zmiennych w zakresie i poza zakresem. Zapobiega to zawieszaniu się wskaźników podczas cofania alokacji zmiennych.

Razem cechy i okresy istnienia pomagają zapewnić bezpieczeństwo typów, bezpieczeństwo pamięci i niezawodność kodu.

Zrozumienie cech w rdzy

Cechy to zbiory metod, które mogą implementować inne typy. Cechy są podobne do interfejsy w językach takich jak Java, Go i TypeScript, ale bardziej elastyczny.

Użyjesz cecha słowo kluczowe do zdefiniowania cech w Rust, po którym następuje deklaracja sygnatur metod.

cechaMoja Cecha {
przypmoja_metoda(&samego siebie);
}
instagram viewer

Kod definiuje cechę o nazwie Moja Cecha z moja_metoda metoda. The &samego siebie parametr wskazuje, że metoda odwołuje się do obiektu typu implementującego jako swojego pierwszego parametru.

Po zdefiniowaniu cechy możesz ją zaimplementować dla swoich typów niestandardowych.

Oto jak możesz zaimplementować cechę dla swoich typów struktur.

strukturaOsoba {
nazwa: Strunowy,
wiek: u32,
}

implik Informacje Do Osoba {
przypstreszczenie(&samego siebie) {
drukuj!(„Nazywam się {} i mam {} lat”., samego siebie.nazwa, samego siebie.wiek);
}
}

The Osoba narzędzia konstrukcyjne Informacje, i możesz zadzwonić do streszczenie metoda na instancjach Osoba struktura.

przypgłówny(){
pozwalać jan = Osoba {imię: Strunowy::z("Jan"), wiek: 30 };
jan.podsumowanie(); // Dane wyjściowe: Nazywam się John i mam 30 lat.
}

The Jan zmienna jest instancją Osoba struktura.

The główny wywołania funkcji streszczenie który wypisuje komunikat do konsoli:

Wyliczenia mogą implementować cechy. Oto jak możesz zdefiniować wyliczenie z wariantami, które implementują streszczenie metoda:

wyliczenieMoje wyliczenie {
Wariant A,
Wariant B,
}

implik Informacje Do Moje wyliczenie {
przypstreszczenie(&samego siebie) {
meczsamego siebie {
MyEnum:: Wariant A => {
// implementacja dla wariantu A
}
MyEnum:: VariantB => {
// implementacja dla wariantu B
}
}
}
}

Używanie cech dla parametrów funkcji i wartości zwracanych

Możesz używać cech jako parametrów funkcji i wartości zwracanych. Używanie cech jako parametrów funkcji jest przydatne przy pisaniu ogólnego kodu z wieloma typami.

Oto funkcja, która przyjmuje parametr dowolnego typu, który implementuje Informacje.

przypZrób coś(wartość: T) {
wartość.podsumowanie();
}

The składnia to określa T musi wdrożyć Informacje. Możesz zadzwonić do streszczenie funkcja z dowolną wartością, która implementuje Informacje.

Życie w Rust

Narzędzie do sprawdzania wypożyczeń Rusta analizuje programy i zapewnia prawidłowe wykorzystanie pamięci. w rdzy, każda wartość ma właściciela odpowiada za cofnięcie alokacji wartości. Gdy zmienne pożyczają wartości, zapożyczają odwołanie do przekazanej wartości, ale właściciel zachowuje własność.

Okresy życia to sposób na zapewnienie, że pożyczone wartości są używane poprawnie. Czas życia to etykieta dołączona do referencji, opisująca, jak długo referencja jest ważna.

W Rust możesz określić czas życia za pomocą adnotacji apostrofowej:

funkcja<'A>

Podczas tworzenia referencji przypisywany jest jej czas życia, który opisuje, jak długo jest ważny. Jeśli masz funkcję, która pobiera odwołanie do wartości, czas życia musi być dłuższy niż wywołanie funkcji, aby zapewnić, że wartość jest poprawna, gdy funkcja zwróci.

Oto przykład specyfikacji czasu życia w funkcji.

przypZrób coś<'A>(x: &'Ai32) -> &'Ai32 {
X
}

przypgłówny() {
pozwalać x = 42;
pozwalać wynik = zrób_coś(&x);
drukuj!("Wynik to: {}", wynik);
}

w Zrób coś funkcja, tzw 'A parametr czasu życia wskazuje, że odwołanie do X obowiązuje tak długo, jak długo wywołanie funkcji. Zwrócone odwołanie jest również ważne, o ile wywołanie funkcji.

The główny funkcja drukuje wynik, przekazując odwołanie do X zmienna w główny funkcję do konsoli.

Składnia okresu istnienia może być pełna, ale jest niezbędna do zarządzania bezpieczeństwem i pamięcią. Reguły elizji trzech okresów życia zapewniają wytyczne, które pozwalają Rustowi wnioskować o czasie życia odniesień w określonych sytuacjach.

Reguła czasu życia danych wejściowych

Reguła czasu życia danych wejściowych określa, że ​​jeśli funkcja lub metoda przyjmuje jedną lub więcej referencji jako parametry wejściowe, Rust zakłada, że ​​wszystkie referencje mają ten sam czas życia.

Mówiąc najprościej, czas życia odniesień wyjściowych będzie taki sam jak odniesień wejściowych.

przypnajdłuższy<'A>(x: &'Aul, y: &'Aul) -> &'Aul {
Jeśli x.len() > y.len() { x } w przeciwnym razie {y}
}

w najdłuższy funkcji, Rust wnioskuje, że czas życia odniesienia wyjściowego jest taki sam jak odniesienia wejściowego, ponieważ oba mają ten sam parametr czasu życia 'A.

Reguła czasu życia danych wejściowych ułatwia pisanie funkcji ogólnych, które jako dane wejściowe przyjmują wiele odniesień.

Reguła czasu życia danych wyjściowych

Reguła czasu życia danych wyjściowych określa, że ​​jeśli funkcja lub metoda zwróci referencję, Rust założy, że czas życia referencji wyjściowej różni się od czasu życia jakiejkolwiek referencji wejściowej.

przyppierwsze słowo<'A>(s: &'Aul) -> &'Aul {
s.split_whitespace().next().unwrap()
}

W tej funkcji Rust wnioskuje, że czas życia odniesienia wyjściowego różni się od czasu życia odniesienia wejściowego, ponieważ split_whitespace() Metoda tworzy odwołanie wyjściowe, które nie przyjmuje parametrów odwołania wejściowego.

Reguła eliminacji życia

Reguła elizji czasów życia ma zastosowanie, jeśli funkcja lub metoda przyjmuje jedną referencję lub parametr wejściowy i zwraca referencję. W takim przypadku Rust zakłada, że ​​referencja wyjściowa ma taki sam czas życia jak referencja wejściowa.

przypnajdłuższy<'A>(x: &'Aul, y: &ul) -> &'Aul {
Jeśli x.len() > y.len() { x } w przeciwnym razie {y}
}

W tej funkcji Rust wnioskuje, że czas życia referencji wyjściowej jest taki sam jak czas życia referencji wejściowej, ponieważ referencja wejściowa y nie ma parametru żywotności. Rdza pomija parametr żywotności dla y i zakłada, że ​​ma taki sam czas życia jak X.

Ta reguła ułatwia pisanie funkcji, które pobierają jedno odwołanie wejściowe i zwracają jedno odwołanie wyjściowe.

Cechy i żywotność

Możesz łączyć cechy i okresy istnienia, aby tworzyć funkcje ogólne, które działają dla typów, które implementują cechę i mają prawidłowy okres istnienia.

Oto cecha i funkcja odwołująca się do wartości, która implementuje tę cechę.

cechaToString {
przypdo_ciągu(&samego siebie) -> Strunowy;
}

przypdo_ciągu<'A, T: ToString>(t: &'A T) -> Strunowy {
t.to_string()
}

Tutaj parametr żywotności 'A gwarantuje, że odniesienie T jest ważny przez cały okres istnienia obiektu, do którego się odwołuje. Możesz użyć do_ciągu funkcja z typami, które implementują ToString cecha mająca ważny czas życia.

Cechy stanowią podstawę implementacji koncepcji OOP w Rust

Cechy umożliwiają definiowanie zachowań. Chociaż Rust nie jest obiektowym językiem programowania (OOP), możesz użyć cech do implementacji koncepcji OOP, od enkapsulacji po dziedziczenie, polimorfizm i abstrakcję.

Implementacja tych koncepcji OOP z cechami sprawia, że ​​programy Rust są skalowalne, solidne, łatwe w utrzymaniu i wydajne.