Como você usa as importações do módulo pai no Rust?

Se você tem uma estrutura de diretórios como esta:

src/main.rs src/module1/blah.rs src/module1/blah2.rs src/utils/logging.rs 

Como você usa funções de outros arquivos?

Do tutorial do Rust, parece que eu deveria ser capaz de fazer isso:

main.rs

 mod utils { pub mod logging; } mod module1 { pub mod blah; } fn main() { utils::logging::trace("Logging works"); module1::blah::doit(); } 

logging.rs

 pub fn trace(msg: &str) { println!(": {}\n", msg); } 

blah.rs

 mod blah2; pub fn doit() { blah2::doit(); } 

blah2.rs

 mod utils { pub mod logging; } pub fn doit() { utils::logging::trace("Blah2 invoked"); } 

No entanto, isso produz um erro:

 error[E0583]: file not found for module `logging` --> src/main.rs:1:21 | 1 | mod utils { pub mod logging; } | ^^^^^^^ | = help: name the file either logging.rs or logging/mod.rs inside the directory "src/utils" 

Parece que importar o caminho, ou seja, de main para module1/blah.rs funciona e importar peers, ou seja, blah2 de blah funciona, mas a importação do escopo pai não funciona.

Se eu usar a diretiva #[path] mágico #[path] , posso fazer isso funcionar:

blah2.rs

 #[path="../utils/logging.rs"] mod logging; pub fn doit() { logging::trace("Blah2 invoked"); } 

Eu realmente tenho que usar manualmente os caminhos de arquivo relativos para importar algo de um nível de escopo pai? Não há melhor maneira de fazer isso em Rust?

Em Python, você usa from .blah import x para o escopo local, mas se você quiser acessar um caminho absoluto, você pode usar a from project.namespace.blah import x .

Eu estou supondo que você deseja declarar utils e utils::logging no nível superior, e apenas deseja chamar funções deles dentro module1::blah::blah2 . A declaração de um módulo é feita com mod , que o insere no AST e define seu caminho canônico foo::bar::baz style, e interações normais com um módulo (longe da declaração) são feitas com o use .

 // main.rs mod utils { pub mod logging { // could be placed in utils/logging.rs pub fn trace(msg: &str) { println!(": {}\n", msg); } } } mod module1 { pub mod blah { // in module1/blah.rs mod blah2 { // in module1/blah2.rs // *** this line is the key, to bring utils into scope *** use utils; pub fn doit() { utils::logging::trace("Blah2 invoked"); } } pub fn doit() { blah2::doit(); } } } fn main() { utils::logging::trace("Logging works"); module1::blah::doit(); } 

A única mudança que fiz foi a linha blah2 use utils em blah2 . Veja também a segunda metade desta resposta para mais detalhes sobre como funciona o use . A seção relevante do The Rust Programming Language também é uma referência razoável, em particular essas duas subseções:

Além disso, observe que eu escrevo tudo em linha, colocando o conteúdo de foo/bar.rs no mod foo { mod bar { } } , mudando isso para mod foo { mod bar; } mod foo { mod bar; } com o arquivo relevante disponível deve ser idêntico.

(By the way, println(": {}\n", msg) imprime duas novas linhas; println! Inclui um já (o ln é “linha”), quer print!(": {}\n", msg) ou println!(": {}", msg) imprime apenas um.


Para obter a estrutura exata desejada:

main.rs

 mod utils { pub mod logging; } mod module1 { pub mod blah; } fn main() { utils::logging::trace("Logging works"); module1::blah::doit(); } 

utils / logging.rs

 pub fn trace(msg: &str) { println!(": {}\n", msg); } 

module1 / blah.rs

 mod blah2; pub fn doit() { blah2::doit(); } 

module1 / blah2.rs (o único arquivo que requer alterações)

 use utils; // this is the only change pub fn doit() { utils::logging::trace("Blah2 invoked"); } 

Também vou responder a essa pergunta, para qualquer outra pessoa que encontre isso e esteja (como eu) totalmente confusa com as respostas difíceis de compreender.

Tudo se resume a duas coisas que sinto que estão mal explicadas no tutorial:

  • O mod blah; A syntax importa um arquivo para o compilador. Você deve usar isso em todos os arquivos que você deseja compilar .

  • Assim como as bibliotecas compartilhadas, qualquer módulo local definido pode ser importado para o escopo atual usando use blah::blah; .

Um exemplo típico seria:

 src/main.rs src/one/one.rs src/two/two.rs 

Nesse caso, você pode ter o código em one.rs de two.rs usando:

 use two::two; // <-- Imports two::two into the local scope as 'two::' pub fn bar() { println!("one"); two::foo(); } 

No entanto, main.rs terá que ser algo como:

 use one::one::bar; // <-- Use one::one::bar mod one { pub mod one; } // <-- Awkwardly import one.rs as a file to compile. // Notice how we have to awkwardly import two/two.rs even though we don't // actually use it in this file; if we don't, then the compiler will never // load it, and one/one.rs will be unable to resolve two::two. mod two { pub mod two; } fn main() { bar(); } 

Observe que você pode usar o arquivo blah/mod.rs para aliviar um pouco o constrangimento, colocando um arquivo como one/mod.rs , porque mod x; tentativas x.rs e x/mod.rs como cargas.

 // one/mod.rs pub mod one.rs 

Você pode reduzir as importações de arquivos inábil na parte superior de main.rs para:

 use one::one::bar; mod one; // <-- Loads one/mod.rs, which loads one/one.rs. mod two; // <-- This is still awkward since we don't two, but unavoidable. fn main() { bar(); } 

Há um exemplo de projeto fazendo isso no Github .

Vale a pena notar que os módulos são independentes dos arquivos nos quais os blocos de código estão contidos; embora pareça que a única maneira de carregar um arquivo blah.rs é criar um módulo chamado blah , você pode usar o #[path] para contornar isso, se precisar, por algum motivo. Infelizmente, não parece suportar curingas, agregando funções de vários arquivos em um módulo de nível superior é bastante entediante.

Se você criar um arquivo chamado mod.rs , o rustc irá examiná-lo quando importar um módulo. Eu sugeriria que você criasse o arquivo src/utils/mod.rs e fizesse com que seu conteúdo src/utils/mod.rs parecido com isto:

 pub mod logging; 

Então, no main.rs , adicione uma declaração como esta:

 use utils::logging; 

e chamá-lo com

 logging::trace(...); 

ou você poderia fazer

 use utils::logging::trace; ... trace(...); 

Basicamente, declare seu módulo no arquivo mod.rs e use -o em seus arquivos de origem.