Quero ter um módulo com várias estruturas, cada uma em seu próprio arquivo. Usando um Math
módulo como exemplo:
Math/
Vector.rs
Matrix.rs
Complex.rs
Quero que cada estrutura esteja no mesmo módulo, que usaria do meu arquivo principal, assim:
use Math::Vector;
fn main() {
// ...
}
No entanto, o sistema de módulos do Rust (que é um pouco confuso para começar) não fornece uma maneira óbvia de fazer isso. Parece que só permite que você tenha o módulo inteiro em um arquivo. Isso não é rústico? Se não, como faço isso?
foo::bar::Baz
deve ser definido emfoo/bar.rs
oufoo/bar/mod.rs
.Respostas:
O sistema de módulos do Rust é incrivelmente flexível e permite que você exponha qualquer tipo de estrutura que desejar, enquanto oculta como seu código está estruturado em arquivos.
Acho que a chave aqui é fazer uso de
pub use
, o que permitirá que você reexporte identificadores de outros módulos. Há precedentes para isso nastd::io
caixa de Rust, onde alguns tipos de submódulos são reexportados para uso emstd::io
.Para adaptar seu exemplo, podemos começar com esta estrutura de diretório:
Aqui está o seu
main.rs
:E seu
src/lib.rs
:E finalmente
src/vector.rs
:E é aqui que a mágica acontece. Definimos um submódulo
math::vector::vector_a
que tem alguma implementação de um tipo especial de vetor. Mas não queremos que os clientes de sua biblioteca se importem que haja umvector_a
submódulo. Em vez disso, gostaríamos de disponibilizá-lo nomath::vector
módulo. Isso é feito com opub use self::vector_a::VectorA
, que reexporta ovector_a::VectorA
identificador no módulo atual.Mas você perguntou como fazer isso para que pudesse colocar suas implementações vetoriais especiais em arquivos diferentes. É isso que a
mod vector_b;
linha faz. Ele instrui o compilador Rust a procurar umvector_b.rs
arquivo para a implementação desse módulo. E com certeza, aqui está nossosrc/vector_b.rs
arquivo:Do ponto de vista do cliente, o fato de
VectorA
eVectorB
serem definidos em dois módulos diferentes em dois arquivos diferentes é totalmente opaco.Se você estiver no mesmo diretório que
main.rs
, deverá ser capaz de executá-lo com:Em geral, o capítulo "Caixas e Módulos" no livro Rust é muito bom. Existem muitos exemplos.
Finalmente, o compilador Rust também procura em subdiretórios para você automaticamente. Por exemplo, o código acima funcionará inalterado com esta estrutura de diretório:
Os comandos para compilar e executar também permanecem os mesmos.
fonte
math::Vec2
vez demath::vector::Vec2
. (ou seja, mesmo conceito, mas um módulo mais profundo.)As regras do módulo Rust são:
O arquivo matrix.rs 1 no diretório math é apenas o módulo
math::matrix
. É fácil. O que você vê em seu sistema de arquivos, também encontra em seu código-fonte. Esta é uma correspondência de um para um de caminhos de arquivo e caminhos de módulo 2 .Então você pode importar uma estrutura
Matrix
comuse math::matrix::Matrix
, porque a estrutura está dentro do arquivo matrix.rs em um diretório matemático. Infeliz? Você prefereuse math::Matrix;
muito, não é? É possível.math::matrix::Matrix
Exporte novamente o identificador em math / mod.rs com:Há outra etapa para fazer isso funcionar. Rust precisa de uma declaração de módulo para carregar o módulo. Adicione um
mod math;
em main.rs. Se você não fizer isso, receberá uma mensagem de erro do compilador ao importar assim:A dica é enganosa aqui. Não há necessidade de caixas adicionais, exceto, é claro, que você realmente pretende escrever uma biblioteca separada.
Adicione isso no topo de main.rs:
A declaração do módulo também é necessária para os submódulos
vector
,matrix
ecomplex
, porquemath
precisa carregá-los para reexportá-los. A reexportação de um identificador só funciona se você carregou o módulo do identificador. Isso significa que, para reexportar o identificador,math::matrix::Matrix
você precisa escrevermod matrix;
. Você pode fazer isso em math / mod.rs. Portanto, crie o arquivo com este conteúdo:Aaaand você está feito.
1 Os nomes dos arquivos de origem geralmente começam com uma letra minúscula em Rust. É por isso que uso matrix.rs e não Matrix.rs.
2 Java é diferente. Você declara o caminho com
package
também. É redundante. O caminho já é evidente a partir da localização do arquivo de origem no sistema de arquivos. Por que repetir essas informações em uma declaração no início do arquivo? Claro que às vezes é mais fácil dar uma olhada rápida no código-fonte em vez de descobrir a localização do arquivo no sistema de arquivos. Posso entender as pessoas que dizem que é menos confuso.fonte
Os puristas de Rusts provavelmente me chamarão de herege e odiarão essa solução, mas isso é muito mais simples: basta fazer cada coisa em seu próprio arquivo e, em seguida, usar a macro " incluir! " No mod.rs:
Dessa forma, você não obtém módulos aninhados adicionados e evita regras complicadas de exportação e reescrita. Simples, eficaz, sem complicações.
fonte
use super::*
). Você não pode ocultar o código de outros arquivos (o que é importante para abstrações seguras de uso inseguro)Tudo bem, lutei com meu compilador por um tempo e finalmente consegui fazê-lo funcionar (obrigado a BurntSushi por apontar
pub use
.main.rs:
math / mod.rs:
math / vector.rs
Outras estruturas podem ser adicionadas da mesma maneira. NOTA: compilado com 0.9, não mestre.
fonte
mod math;
emmain.rs
casais seumain
programa com a sua biblioteca. Se você quiser que seumath
módulo seja independente, você precisará compilá-lo separadamente e vinculá-lo aextern crate math
(conforme mostrado em minha resposta). No Rust 0.9, é possível que a sintaxe sejaextern mod math
.Eu gostaria de adicionar aqui como você inclui arquivos Rust quando eles estão profundamente aninhados. Tenho a seguinte estrutura:
Como você acessa
sink.rs
outoilet.rs
demain.rs
?Como outros já mencionaram, Rust não tem conhecimento de arquivos. Em vez disso, ele vê tudo como módulos e submódulos. Para acessar os arquivos dentro do diretório do banheiro, você precisa exportá-los ou colocá-los no topo. Você faz isso especificando um nome de arquivo com o diretório que deseja acessar e
pub mod filename_inside_the_dir_without_rs_ext
dentro do arquivo.Exemplo.
Crie um arquivo chamado
bathroom.rs
dentro dohome
diretório:Exporte os nomes dos arquivos:
Crie um arquivo chamado
home.rs
próximo amain.rs
pub mod
o arquivo bathroom.rsDentro
main.rs
use
declarações também podem ser usadas:Incluindo outros módulos irmãos (arquivos) dentro de submódulos
No caso de desejar usar
sink.rs
detoilet.rs
, você pode chamar o módulo especificando as palavras-chaveself
ousuper
.Estrutura Final do Diretório
Você acabaria com algo assim:
A estrutura acima só funciona com o Rust 2018 em diante. A seguinte estrutura de diretório também é válida para 2018, mas é como 2015 costumava funcionar.
Em que
home/mod.rs
é o mesmo que./home.rs
ehome/bathroom/mod.rs
é o mesmo quehome/bathroom.rs
. Rust fez essa alteração porque o compilador ficaria confuso se você incluísse um arquivo com o mesmo nome do diretório. A versão 2018 (a mostrada primeiro) corrige essa estrutura.Veja este repositório para obter mais informações e este vídeo do YouTube para uma explicação geral.
Uma última coisa ... evite hífens! Use em seu
snake_case
lugar.Nota importante
Você deve colocar todos os arquivos no topo, mesmo se arquivos profundos não forem exigidos pelos de nível superior.
Isso significa que, para
sink.rs
descobrirtoilet.rs
, você precisa barrá-los usando os métodos acima atémain.rs
!Em outras palavras, fazer
pub mod sink;
ouuse self::sink;
dentrotoilet.rs
vai não trabalho a menos que você tê-los expostos por todo o caminho atémain.rs
!Portanto, lembre-se sempre de colocar seus arquivos no topo!
fonte