Historycznie Rust od samego początku był projektowany jako język do pisania skalowalnych, niezawodnych programów systemowych. System modułów został stworzony w celu rygorystycznego rozdzielenia przestrzeni nazw, izolacji kodu i organizacji dużych projektów bez konfliktów nazw. Moduły pozwalają na strukturyzację kodu, ukrywanie szczegółów implementacji i zarządzanie publicznym API.
Problem polega na tym, że wraz z rozwojem projektu pojawia się kwestia izolacji komponentów, współdzielenia kodu i zarządzania widocznością. Błędy często związane są z niewłaściwym użyciem słów kluczowych pub, pub(crate), a także z brakiem lub niewłaściwą organizacją struktury katalogów i plików.
Rozwiązanie: w Rust moduł jest zadeklarowany za pomocą słowa kluczowego mod; pliki i katalogi strukturyzują przestrzeń nazw, podkreślając hierarchię. Wszystko jest prywatne domyślnie, a słowa kluczowe pub, pub(crate), pub(super) pozwalają zarządzać widocznością. Poprawne użycie eksportów, aliasów oraz mądre podzielanie projektu na moduły sprawia, że kod staje się skalowalny, łatwy w utrzymaniu i bezpieczny.
Przykład kodu:
// src/lib.rs mod network; pub use network::client::Client; // src/network/mod.rs pub mod client; // src/network/client.rs pub struct Client { pub name: String, }
Kluczowe cechy:
Co się stanie, jeśli struktura katalogu nie odpowiada definicjom modułów?
Kod nie skompiluje się: Rust oczekuje ścisłego dopasowania plików i modułów (na przykład, jeśli zadeklarowano mod foo, to musi istnieć plik foo.rs lub katalog foo/mod.rs).
Czy pub(crate) może być używane do ukrywania implementacji przed zewnętrznymi użytkownikami, ale będzie dostępne wszystkim modułom wewnątrz crate?
Tak, pub(crate) czyni element widocznym w każdym module tego samego crate, ale niewidocznym dla zewnętrznych projektów używających tego crate jako zależności.
Jak importować funkcje z głęboko zagnieżdżonego modułu do pliku rootowego bez publicznego reekspozycjonowania (pub use)?
Przez bezpośrednie odwołanie w hierarchii: crate::module::submodule::function. Bez pub use będą dostępne tylko w obrębie crate.
W projekcie moduł sieci zawiera wszystkie komponenty serwera, klienta, parsera protokołów w jednym pliku network.rs. Wszystko zadeklarowane jako pub, do testowania podłączony jest bezpośrednio wewnętrzny kod. Z czasem wzrasta niepotrzebna powiązanie i trudno oddzielić publiczny interfejs od implementacji.
Zalety:
Wady:
Sieć podzielona na submoduły: client, server, protocol. Tylko publiczne interfejsy są eksportowane z network, API jest kompaktowe, szczegóły są enkapsulowane. Testy dla każdego modułu są izolowane.
Zalety:
Wady: