Chcę mieć moduł z wieloma strukturami, każda w swoim własnym pliku. Na przykładzie Math
modułu:
Math/
Vector.rs
Matrix.rs
Complex.rs
Chcę, aby każda struktura znajdowała się w tym samym module, którego użyłbym z mojego głównego pliku, na przykład:
use Math::Vector;
fn main() {
// ...
}
Jednak system modułów Rusta (który na początku jest nieco zagmatwany) nie zapewnia oczywistego sposobu, aby to zrobić. Wydaje się, że pozwala tylko na posiadanie całego modułu w jednym pliku. Czy to nie rustykalne? Jeśli nie, jak mam to zrobić?
foo::bar::Baz
powinna być zdefiniowana wfoo/bar.rs
lubfoo/bar/mod.rs
.Odpowiedzi:
System modułów Rusta jest w rzeczywistości niesamowicie elastyczny i pozwoli Ci ujawnić dowolną strukturę, ukrywając strukturę kodu w plikach.
Myślę, że kluczem jest tutaj wykorzystanie
pub use
, które pozwoli na reeksportowanie identyfikatorów z innych modułów. Jest to precedens wstd::io
skrzynce Rusta, gdzie niektóre typy z podmodułów są ponownie eksportowane do użytku wstd::io
.Aby dostosować Twój przykład, możemy zacząć od tej struktury katalogów:
Oto twoje
main.rs
:I twój
src/lib.rs
:I wreszcie
src/vector.rs
:I tu dzieje się magia. Zdefiniowaliśmy podmoduł,
math::vector::vector_a
który ma pewną implementację specjalnego rodzaju wektora. Ale nie chcemy, aby klienci Twojej biblioteki przejmowali się tym, że istniejevector_a
podmoduł. Zamiast tego chcielibyśmy udostępnić go wmath::vector
module. Odbywa się to za pomocąpub use self::vector_a::VectorA
, który ponownie eksportujevector_a::VectorA
identyfikator w bieżącym module.Ale zapytałeś, jak to zrobić, aby móc umieścić swoje specjalne implementacje wektorowe w różnych plikach. To właśnie
mod vector_b;
robi linia. Instruuje kompilator Rusta, aby szukałvector_b.rs
pliku do implementacji tego modułu. I rzeczywiście, oto naszsrc/vector_b.rs
plik:Z punktu widzenia klienta fakt, że
VectorA
iVectorB
są zdefiniowane w dwóch różnych modułach w dwóch różnych plikach, jest całkowicie nieprzejrzysty.Jeśli jesteś w tym samym katalogu
main.rs
, powinieneś móc go uruchomić za pomocą:Ogólnie rozdział „Skrzynie i moduły” w książce Rust jest całkiem niezły. Jest wiele przykładów.
Wreszcie, kompilator Rust automatycznie szuka również podkatalogów. Na przykład powyższy kod będzie działał bez zmian w tej strukturze katalogów:
Polecenia kompilacji i uruchomienia również pozostają takie same.
źródło
math::Vec2
zamiastmath::vector::Vec2
. (tj. ta sama koncepcja, ale o jeden moduł głębiej)Zasady modułu Rust to:
Plik matrix.rs 1 w katalogu math to tylko moduł
math::matrix
. To jest łatwe. To, co widzisz w swoim systemie plików, znajdziesz również w kodzie źródłowym. Jest to zgodność jeden do jednego ścieżek plików i ścieżek modułów 2 .Możesz więc zaimportować strukturę za
Matrix
pomocąuse math::matrix::Matrix
, ponieważ struktura znajduje się w pliku matrix.rs w katalogu matematycznym. Nieszczęśliwy? Zamiast tego woliszuse math::Matrix;
bardzo dużo, prawda? To jest możliwe. Ponownie wyeksportuj identyfikatormath::matrix::Matrix
w math / mod.rs z:Jest jeszcze jeden krok, aby to zadziałało. Rust potrzebuje deklaracji modułu, aby załadować moduł. Dodaj
mod math;
w main.rs. Jeśli tego nie zrobisz, kompilator wyświetli komunikat o błędzie podczas importowania:Wskazówka jest tutaj myląca. Nie ma potrzeby tworzenia dodatkowych skrzynek, z wyjątkiem oczywiście, że naprawdę zamierzasz napisać oddzielną bibliotekę.
Dodaj to na górze main.rs:
Deklaracja modułu jest również niezbędna dla podmodułów
vector
,matrix
acomplex
ponieważmath
trzeba je załadować, aby je ponownie wyeksportować. Ponowny eksport identyfikatora działa tylko wtedy, gdy załadowałeś moduł identyfikatora. Oznacza to, że aby ponownie wyeksportować identyfikator,math::matrix::Matrix
który musisz zapisaćmod matrix;
. Możesz to zrobić w math / mod.rs. Dlatego utwórz plik z następującą zawartością:Aaa i gotowe.
1 Nazwy plików źródłowych zwykle rozpoczynają się małą literą w Rust. Dlatego używam matrix.rs, a nie Matrix.rs.
2 Java jest inna. Ty też deklarujesz ścieżkę
package
. To jest zbędne. Ścieżka jest już widoczna w lokalizacji pliku źródłowego w systemie plików. Po co powtarzać tę informację w deklaracji na początku pliku? Oczywiście czasami łatwiej jest rzucić okiem na kod źródłowy, zamiast znaleźć lokalizację pliku w systemie plików. Rozumiem ludzi, którzy mówią, że to mniej zagmatwane.źródło
Puryści Rustsa prawdopodobnie będą nazywać mnie heretykiem i nienawidzą tego rozwiązania, ale jest to znacznie prostsze: po prostu zrób każdą rzecz w swoim własnym pliku, a następnie użyj makra „ include! ” W mod.rs:
W ten sposób nie masz dodanych zagnieżdżonych modułów i unikniesz skomplikowanych reguł eksportu i przepisywania. Prosty, skuteczny, bez problemów.
źródło
use super::*
). Nie możesz ukryć kodu przed innymi plikami (co jest ważne w przypadku niebezpiecznych, bezpiecznych abstrakcji)W porządku, walczyłem przez chwilę z moim kompilatorem i wreszcie udało mi się go uruchomić (dzięki BurntSushi za wskazanie
pub use
.main.rs:
matematyka / mod.rs:
matematyka / vector.rs
W ten sam sposób można dodać inne struktury. UWAGA: skompilowane z 0.9, nie master.
źródło
mod math;
wmain.rs
par swoimmain
programie z biblioteki. Jeśli chcesz, aby Twójmath
moduł był niezależny, musisz go skompilować osobno i połączyć z nim za pomocąextern crate math
(jak pokazano w mojej odpowiedzi). W Rust 0.9 możliwe jest, żeextern mod math
zamiast tego składnia jest .Chciałbym tutaj dodać, jak włącza się pliki Rusta, gdy są głęboko zagnieżdżone. Mam następującą strukturę:
Jak uzyskujesz dostęp
sink.rs
lubtoilet.rs
skądmain.rs
?Jak wspominali inni, Rust nie ma wiedzy o plikach. Zamiast tego widzi wszystko jako moduły i podmoduły. Aby uzyskać dostęp do plików w katalogu bathroom, musisz je wyeksportować lub umieścić na górze. Robisz to, określając nazwę pliku z katalogiem, do którego chcesz uzyskać dostęp, i
pub mod filename_inside_the_dir_without_rs_ext
wewnątrz pliku.Przykład.
Utwórz plik o nazwie
bathroom.rs
wewnątrzhome
katalogu:Wyeksportuj nazwy plików:
Utwórz plik o nazwie
home.rs
obokmain.rs
pub mod
plik bathroom.rsW ciągu
main.rs
use
można również użyć instrukcji:Włączanie innych siostrzanych modułów (plików) w podmodułach
W przypadku, gdy chcesz użyć
sink.rs
fromtoilet.rs
, możesz wywołać moduł, określając słowa kluczoweself
lubsuper
.Ostateczna struktura katalogów
Skończyłbyś z czymś takim:
Powyższa struktura działa tylko od wersji Rust 2018. Poniższa struktura katalogów obowiązuje również w 2018 r., Ale tak było w 2015 r.
W którym
home/mod.rs
jest tym samym co./home.rs
ihome/bathroom/mod.rs
jest tym samym cohome/bathroom.rs
. Rust wprowadził tę zmianę, ponieważ kompilator byłby zdezorientowany, gdyby dołączył plik o tej samej nazwie co katalog. Wersja 2018 (ta pokazana jako pierwsza) naprawia tę strukturę.Zobacz to repozytorium, aby uzyskać więcej informacji, i ten film na YouTube, aby uzyskać ogólne wyjaśnienie.
Ostatnia rzecz ... unikaj myślników! Użyj
snake_case
zamiast tego.Ważna uwaga
Państwo musi baryłkę wszystkie pliki na górę, nawet jeśli głębokie pliki nie są wymagane przez nich najwyższego poziomu.
Oznacza to, że aby
sink.rs
je odkryćtoilet.rs
, musiałbyś je zlikwidować, stosując powyższe metody aż domain.rs
!Innymi słowy, robi
pub mod sink;
lubuse self::sink;
wewnątrztoilet.rs
będzie działać , chyba że wystawiony im przez całą drogę aż domain.rs
!Dlatego zawsze pamiętaj, aby wbić pliki do góry!
źródło