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 modył
m2
nie musi być publiczny chociaż jest wykorzystywany w funkcjim1_function
? - Dlaczego funkcja
m2_function
musi być publiczna, aby mogła zostać wykorzystana przez funkcjęm1_function
? - Czy funkcję
m2_function
można wywołać w funkcjimain
? Dlaczego? - Dlaczego funkcja
m2_function
moż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
- dashboard
Rust 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łmodels
i 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łyparticipant
iowner
.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łservices
i 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-quizzes
za 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.rs
i zaimplementuj je w odpowiednich plikach modułów. -
Uruchom program za pomocą komendy
cargo run
i sprawdź, czy wszystko działa poprawnie.