Moduły
W języku Rust moduły służą do organizacji kodu w logiczne jednostki. Moduły pozwalają na grupowanie powiązanych funkcji, struktur, typów i stałych, co ułatwia zarządzanie kodem i jego ponowne wykorzystanie.
Moduły możemy tworzyć w dowolnym miejscu w pliku źródłowym, ale zazwyczaj definiuje się je w osobnych plikach. Moduły mogą być zagnieżdżone, co pozwala na tworzenie hierarchii modułów.
Tworzenie i używanie modułów
Dla zobrazowania działania modułów zdefiniujemy moduły w ramach jednego pliku. W dalszej części konspektu dowiesz się jak moduły mogą być zdefiniowane w osobnych plikach (co jest domyślnym sposobem wykorzystania modułów w większych projektach).
-
Moduł tworzy się za pomocą słowa kluczowego
mod:mod my_module { pub fn my_function() { println!("Hello from my_module!"); } pub mod my_submodule { pub fn my_subfunction() { println!("Hello from my_submodule!"); } } } -
Aby użyć funkcji zdefiniowanej w module, należy odwołać się do niej za pomocą ścieżki modułu. Na poniższym przykładzie podano ścieżkę względną (odwołując się z poziomu bieżącego modułu — w naszym przypadku jest to moduł główny zdefiniowany implicite w pliku
main.rs):fn main() { my_module::my_function(); my_module::my_submodule::my_subfunction(); } -
Do odwołania się do modułów można również wykorzystać ścieżki bezwzględne, zaczynając od głównego modułu projektu (ang. root module). W takiej sytuacji zaczynamy ścieżkę od słowa kluczowego
crate:fn main() { crate::my_module::my_function(); crate::my_module::my_submodule::my_subfunction(); } -
Elementy modułów można również importować do przestrzeni nazw za pomocą słowa kluczowego
use:use my_module::my_function; use my_module::my_submodule::my_subfunction; fn main() { my_function(); my_subfunction(); }
Reguły prywatności
Domyślnie elementy takie jak funkcje, struktury i moduły są prywatne. Aby dany element stał się publiczny, musimy oznaczyć go słowem kluczowym pub.
Reguły dotyczące widoczności elementów:
- Jeśli element jest publiczny, dostęp do niego można uzyskać przez dowolny jego moduł nadrzędny.
- Jeśli element jest prywatny, dostęp do niego można uzyskać tylko przez najbliższy moduł nadrzędny i dowolny moduł podrzędny tego modułu nadrzędnego.
Przeanalizuj poniższy kod i zidentyfikuj działanie powyższych reguł. Odpowiedz na pytania:
- Dlaczego moduł
m2nie musi być publiczny chociaż jest wykorzystywany w funkcjim1_function? - Dlaczego funkcja
m2_functionmusi być publiczna, aby mogła zostać wykorzystana przez funkcjęm1_function? - Czy funkcję
m2_functionmożna wywołać w funkcjimain? Dlaczego? - Dlaczego funkcja
m2_functionmoże wywołać funkcjęm0_function?
mod m1 {
pub fn m1_function() {
println!("Hello from m1_function!");
m2::m2_function();
}
mod m2 {
pub fn m2_function() {
println!("Hello from m2_function!");
crate::m0_function();
}
}
}
fn m0_function() {
println!("Hello from m0_function!");
}
fn main() {
m1::m1_function_a();
m1::m2::m2_function_a();
}Sprawdź się.
Zanim skompilujesz poniższy kod, odpowiedz, które wiersze w funkcji main mają błędy i dlaczego. Następnie skompiluj kod i sprawdź swoje odpowiedzi.
mod outermost {
pub fn middle_function() {}
fn middle_secret_function() {}
mod inside {
pub fn inner_function() {}
fn inner_secret_function() {}
}
}
fn main() {
outermost::middle_function();
outermost::middle_secret_function();
outermost::inside::inner_function();
outermost::inside::inner_secret_function();
}Moduły w osobnych plikach
Rozważmy przykładową hierarchię modułów dla aplikacji umożliwiającej tworzenie i przeprowadzanie quizów (a’la Kahoot). W takim przypadku hierarchia modułów może wyglądać następująco:
- models
- user
- participant
- owner
- quiz
- services
- auth
- quiz
- dashboardRust umożliwia tworzenie modułów w osobnych plikach. Poniżej znajduje się lista plików źródłowych w języku Rust, które odpowiadają podanej hierarchii modułów. Każdy moduł jest reprezentowany przez katalog lub plik zgodnie z systemem modułów w Rust.
Struktura plików w projekcie powinna wyglądać następująco:
- main.rs
- mod.rs
- mod.rs
- participant.rs
- owner.rs
- quiz.rs
- mod.rs
- auth.rs
- quiz.rs
- dashboard.rs
- Cargo.toml
Wyjaśnienie struktury katalogów
src/main.rs: Główny plik projektu, który pełni rolę punktu wejścia aplikacji. W przypadku bibliotek, głównym plikiem jest pliklib.rs.src/models/: Katalog zawierający moduły związane z modelami danych - nazwa katalogu określa nazwę modułu.mod.rs: Plik definiujący modułmodelsi deklarujący jego podmoduły. Nazwa pliku jest zarezerwowana dla modułu głównego w katalogu.user/: Podkatalog dla modułuuser, który zawiera:mod.rs: Deklaruje podmodułyparticipantiowner.participant.rs: Plik zawierający definicję modułuparticipant.owner.rs: Plik zawierający definicję modułuowner.
quiz.rs: Plik zawierający definicję modułuquiz.
src/services/: Katalog zawierający moduły związane z logiką aplikacji (np. usługi).mod.rs: Plik definiujący modułservicesi deklarujący jego podmoduły.auth.rs: Plik zawierający definicję modułuauth.quiz.rs: Plik zawierający definicję modułuquiz.dashboard.rs: Plik zawierający definicję modułudashboard.
Cargo.toml: Plik konfiguracyjny projektu Rust, zawierający informacje o zależnościach, metadanych projektu i ustawieniach kompilacji.
Moduły znajdujące się w osobnych plikach muszą być zadeklarowane w module nadrzędnym. Na przykład, w głównym module (plik main.rs lub lib.rs) musimy zadeklarować moduły models i services:
mod models;
mod services;
fn main() {
models::user::participant::create_participant();
services::auth::login();
}Podobnie, w pliku models/mod.rs musimy zadeklarować moduły user i quiz:
pub mod user;
pub mod quiz;Ćwiczenie
-
Utwórz nowy projekt o nazwie
rust-quizzesza pomocą Cargo:cargo new rust-quizzes -
Odtwórz powyższą strukturę modułów z wykorzystaniem osobnych plików.
-
Utwórz przykładowe metody wskazane w pliku
main.rsi zaimplementuj je w odpowiednich plikach modułów. -
Uruchom program za pomocą komendy
cargo runi sprawdź, czy wszystko działa poprawnie.