简体   繁体   English

你如何在 Rust 中使用父模块导入?

[英]How do you use parent module imports in Rust?

If you have a directory structure like this:如果您有这样的目录结构:

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

How do you use functions from other files?您如何使用其他文件中的函数?

From the Rust tutorial, it sounds like I should be able to do this:从 Rust 教程中,听起来我应该能够做到这一点:

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 blah2.rs

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

However, this produces an error:但是,这会产生错误:

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"

It appears that importing down the path, ie from main to module1/blah.rs works, and importing peers, ie blah2 from blah works, but importing from the parent scope doesn't.似乎沿着路径导入,即从mainmodule1/blah.rs工作,并导入对等点,即blah2blah工作,但从父范围导入没有。

If I use the magical #[path] directive, I can make this work:如果我使用神奇的#[path]指令,我可以完成这项工作:

blah2.rs blah2.rs

#[path="../utils/logging.rs"]
mod logging;

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

Do I really have to manually use relative file paths to import something from a parent scope level?我真的必须手动使用相对文件路径从父范围级别导入内容吗? Isn't there some better way of doing this in Rust?在 Rust 中没有更好的方法来做到这一点吗?

In Python, you use from .blah import x for the local scope, but if you want to access an absolute path you can use from project.namespace.blah import x .在 Python 中,您将from .blah import x用于本地范围,但如果您想访问绝对路径,您可以使用from project.namespace.blah import x

I'm going to answer this question too, for anyone else who finds this and is (like me) totally confused by the difficult-to-comprehend answers.我也将回答这个问题,对于其他发现这个问题并且(像我一样)被难以理解的答案完全困惑的人。

It boils down to two things I feel are poorly explained in the tutorial:归结为我觉得教程中没有很好解释的两件事:

  • The mod blah; mod blah; syntax imports a file for the compiler.语法为编译器导入一个文件。 You must use this on all the files you want to compile .必须在要编译的所有文件上使用它

  • As well as shared libraries, any local module that is defined can be imported into the current scope using use blah::blah;除了共享库之外,任何定义的本地模块都可以使用use blah::blah;导入到当前作用域中use blah::blah; . .

A typical example would be:一个典型的例子是:

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

In this case, you can have code in one.rs from two.rs by using use :在这种情况下,您可以使用use将代码放在one.rstwo.rs

use two::two;  // <-- Imports two::two into the local scope as 'two::'

pub fn bar() {
    println!("one");
    two::foo();
}

However, main.rs will have to be something like:但是, main.rs必须是这样的:

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();
}

Notice that you can use the blah/mod.rs file to somewhat alleviate the awkwardness, by placing a file like one/mod.rs , because mod x;请注意,您可以使用blah/mod.rs文件,通过放置像one/mod.rs这样的文件来稍微减轻尴尬,因为mod x; attempts x.rs and x/mod.rs as loads.尝试将x.rsx/mod.rs作为负载。

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

You can reduce the awkward file imports at the top of main.rs to:您可以将 main.rs 顶部的笨拙文件导入减少到:

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();
}

There's an example project doing this on Github .有一个示例项目在Github上执行此操作。

It's worth noting that modules are independent of the files the code blocks are contained in;值得注意的是,模块独立于代码块所在的文件; although it would appear the only way to load a file blah.rs is to create a module called blah , you can use the #[path] to get around this, if you need to for some reason.尽管似乎加载文件blah.rs的唯一方法是创建一个名为blah的模块,但如果出于某种原因需要,您可以使用#[path]来解决此问题。 Unfortunately, it doesn't appear to support wildcards, aggregating functions from multiple files into a top-level module is rather tedious.不幸的是,它似乎不支持通配符,将多个文件中的函数聚合到一个顶级模块中是相当乏味的。

I'm assuming you want to declare utils and utils::logging at the top level, and just wish to call functions from them inside module1::blah::blah2 .我假设您想在顶层声明utilsutils::logging ,并且只想在module1::blah::blah2从它们调用函数。 The declaration of a module is done with mod , which inserts it into the AST and defines its canonical foo::bar::baz -style path, and normal interactions with a module (away from the declaration) are done with use .模块的声明是用mod完成的,它将它插入到 AST 中并定义其规范的foo::bar::baz样式路径,并且与模块的正常交互(远离声明) 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 crate::utils;

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

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

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

The only change I made was the use crate::utils;我所做的唯一更改是use crate::utils; line in blah2 (in Rust 2015 you could also use use utils or use ::utils ). blah2中的blah2 (在 Rust 2015 中,您也可以使用use utilsuse ::utils )。 Also see the second half of this answer for more details on how use works.另请参阅此答案的后半部分,了解有关use如何工作的更多详细信息。 The relevant section of The Rust Programming Language is a reasonable reference too, in particular these two subsections: The Rust Programming Language相关部分也是一个合理的参考,特别是这两个小节:

Also, notice that I write it all inline, placing the contents of foo/bar.rs in mod foo { mod bar { <contents> } } directly, changing this to mod foo { mod bar; }另外,请注意我写的都是内联的,将foo/bar.rs的内容foo/bar.rs放在mod foo { mod bar { <contents> } } ,将其更改为mod foo { mod bar; } mod foo { mod bar; } with the relevant file available should be identical. mod foo { mod bar; }与现有的相关文件应该是相同的。

(By the way, println(": {}\\n", msg) prints two new lines; println! includes one already (the ln is "line"), either print!(": {}\\n", msg) or println!(": {}", msg) print only one.) (顺便说一句, println(": {}\\n", msg)打印两个新行; println!已经包含一个( ln是“line”),要么print!(": {}\\n", msg)println!(": {}", msg)只打印一个。)


It's not idiomatic to get the exact structure you want, you have to make one change to the location of blah2.rs :获得您想要的确切结构并不是惯用的,您必须对blah2.rs的位置进行一次更改:

src
├── main.rs
├── module1
│   ├── blah
│   │   └── blah2.rs
│   └── blah.rs
└── utils
    └── logging.rs

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实用程序/日志记录.rs

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

module1/blah.rs模块 1/blah.rs

mod blah2;

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

module1/blah/blah2.rs (the only file that requires any changes) module1/blah/blah2.rs(唯一需要更改的文件)

// this is the only change

// Rust 2015
// use utils; 

// Rust 2018    
use crate::utils;

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

I realize this is a very old post and probably wasn't using 2018. However, this can still be really tricky and I wanted to help those out that were looking.我意识到这是一篇很老的帖子,可能没有使用 2018 年。但是,这仍然非常棘手,我想帮助那些正在寻找的人。

Because Pictures are worth a thousand words I made this simple for code splitting.因为图片值一千个字,所以我把它简化为代码拆分。

在此处输入图片说明

在此处输入图片说明

Then as you probably guessed they all have an empty pub fn some_function().然后正如您可能猜到的那样,它们都有一个空的 pub fn some_function()。

We can further expand on this via the changes to main我们可以通过更改 main 来进一步扩展这一点在此处输入图片说明

在此处输入图片说明

The additional changes to nested_mod对nested_mod 的额外更改在此处输入图片说明

Let's now go back and answer the question: We added blah1 and blah2 to the mod_1 We added a utils with another mod logging inside it that calls some fn's.现在让我们回过头来回答这个问题: 我们将 blah1 和 blah2 添加到 mod_1 我们添加了一个 utils,其中包含另一个调用 fn 的 mod 日志记录。 Our mod_1/mod.rs now contains:我们的 mod_1/mod.rs 现在包含:

pub mod blah.rs
pub mod blah2.rs

We created a utils/mod.rs used in main containing:我们创建了一个用于 main 的 utils/mod.rs 包含:

pub mod logging

Then a directory called logging/with another mod.rs where we can put fns in logging to import.然后是一个名为 logging/ 与另一个 mod.rs 的目录,我们可以在其中将 fns 放入日志中以进行导入。

在此处输入图片说明

Source also here https://github.com/DavidWhit/Rust_Modules来源也在这里https://github.com/DavidWhit/Rust_Modules

Also Check Chapters 7 for libs example and 14.3 that further expands splitting with workspaces in the Rust Book.另请查看第 7 章中的 libs 示例和 14.3,它进一步扩展了 Rust Book 中工作空间的拆分。 Good Luck!祝你好运!

If you create a file called mod.rs , rustc will look at it when importing a module.如果您创建一个名为mod.rs的文件, rustc将在导入模块时查看它。 I would suggest that you create the file src/utils/mod.rs , and make its contents look something like this:我建议您创建文件src/utils/mod.rs ,并使其内容如下所示:

pub mod logging;

Then, in main.rs , add a statement like this:然后,在main.rs ,添加如下语句:

use utils::logging;

and call it with并调用它

logging::trace(...);

or you could do或者你可以做

use utils::logging::trace;

...

trace(...);

Basically, declare your module in the mod.rs file, and use it in your source files.基本上,在mod.rs文件中声明你的模块,并在你的源文件中use它。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM