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

Dou*_*oug 36 rust

如果您有这样的目录结构:

src/main.rs
src/module1/blah.rs
src/module1/blah2.rs
src/utils/logging.rs
Run Code Online (Sandbox Code Playgroud)

你如何使用其他文件中的函数?

从Rust教程,听起来我应该能够做到这一点:

main.rs

mod utils { pub mod logging; }
mod module1 { pub mod blah; }

fn main() {
    utils::logging::trace("Logging works");
    module1::blah::doit();
}
Run Code Online (Sandbox Code Playgroud)

logging.rs

pub fn trace(msg: &str) {
    println!(": {}\n", msg);
}
Run Code Online (Sandbox Code Playgroud)

blah.rs

mod blah2;
pub fn doit() {
    blah2::doit();
}
Run Code Online (Sandbox Code Playgroud)

blah2.rs

mod utils { pub mod logging; }
pub fn doit() {
    utils::logging::trace("Blah2 invoked");
}
Run Code Online (Sandbox Code Playgroud)

但是,这会产生错误:

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"
Run Code Online (Sandbox Code Playgroud)

看起来导入路径,即从导入mainmodule1/blah.rs工作,导入对等,即blah2blah作品导入,但从父作用域导入不会.

如果我使用魔法#[path]指令,我可以使这项工作:

blah2.rs

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

pub fn doit() {
    logging::trace("Blah2 invoked");
}
Run Code Online (Sandbox Code Playgroud)

我是否真的必须手动使用相对文件路径从父作用域级别导入内容?在Rust中有没有更好的方法呢?

在Python中,您from .blah import x用于本地范围,但是如果要访问可以使用的绝对路径from project.namespace.blah import x.

Dou*_*oug 28

我也要回答这个问题,对于那些发现这个问题并且(像我一样)完全被难以理解的答案搞糊涂的人.

归结为我觉得在教程中解释不好的两件事:

  • mod blah;语法导入文件的编译器.您必须在要编译的所有文件上使用它.

  • 除了共享库之外,还可以使用任何已定义的本地模块导入到当前作用域中use blah::blah;.

一个典型的例子是:

src/main.rs
src/one/one.rs
src/two/two.rs
Run Code Online (Sandbox Code Playgroud)

在这种情况下,你可以有代码one.rstwo.rs使用use:

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

pub fn bar() {
    println!("one");
    two::foo();
}
Run Code Online (Sandbox Code Playgroud)

但是,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();
}
Run Code Online (Sandbox Code Playgroud)

请注意,您可以blah/mod.rs通过放置文件来使用该文件来稍微减轻尴尬one/mod.rs,因为mod x;尝试x.rsx/mod.rs加载.

// one/mod.rs
pub mod one.rs
Run Code Online (Sandbox Code Playgroud)

您可以将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();
}
Run Code Online (Sandbox Code Playgroud)

Github上有一个示例项目.

值得注意的是,模块独立于代码块所包含的文件; 虽然看起来加载文件的唯一方法blah.rs是创建一个名为的模块,如果你出于某种原因需要blah,可以使用它#[path]来解决这个问题.不幸的是,它似乎不支持通配符,将多个文件中的函数聚合到顶级模块中相当繁琐.


huo*_*uon 25

我假设您要声明utilsutils::logging处于顶层,并希望从内部调用函数module1::blah::blah2.完成模块的声明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();
}
Run Code Online (Sandbox Code Playgroud)

我做的唯一改变是use crate::utils;线路blah2.另请参阅本答案的后半部分,了解有关use utils工作原理的更多详细信息.The Rust Programming Language相关部分也是一个合理的参考,特别是这两个小节:

另外,请注意我将其全部内联写入use ::utils,use直接放入内容,将其更改为foo/bar.rs可用的相关文件应该是相同的.

(顺便说一句,mod foo { mod bar { <contents> } }打印了两个新的线; mod foo { mod bar; }包括一个已经(在println(": {}\n", msg)为"线"),无论是println!ln打印唯一的一个.)


要获得您想要的确切结构:

main.rs

src
??? main.rs
??? module1
?   ??? blah
?   ?   ??? blah2.rs
?   ??? blah.rs
??? utils
    ??? logging.rs
Run Code Online (Sandbox Code Playgroud)

utils的/ logging.rs

mod utils {
    pub mod logging;
}

mod module1 {
    pub mod blah;
}

fn main() {
    utils::logging::trace("Logging works");
    module1::blah::doit();
}
Run Code Online (Sandbox Code Playgroud)

模块1/blah.rs

pub fn trace(msg: &str) { 
    println!(": {}\n", msg); 
}
Run Code Online (Sandbox Code Playgroud)

module1/blah2.rs(唯一需要进行任何更改的文件)

mod blah2;

pub fn doit() {
    blah2::doit();
}
Run Code Online (Sandbox Code Playgroud)

  • 请说明如何将其拆分为多个文件;上面的例子是一个“琐碎的例子”;我想要多个文件,这些文件可以访问其他文件中其他模块中定义的功能。来吧,我不是想在这里做些奇怪的事情。我希望将日志记录模块放在一个单独的文件中,放在一个单独的目录中,并且我也想要类似的模块,正如我在问题中的文件布局中所述。“只需将所有内容粘贴到一个文件中,并使用'use utils'并不能解决任何问题。 (4认同)

Jus*_*ave 8

我意识到这是一篇非常旧的帖子,可能没有使用 2018 年。但是,这仍然非常棘手,我想帮助那些正在寻找的人。

因为图片胜过一千个单词,所以我将其简化为代码分割。

在此输入图像描述

在此输入图像描述

然后,正如您可能猜到的那样,它们都有一个空的 pub fn some_function()。

我们可以通过对 main 的更改来进一步扩展这一点 在此输入图像描述

在此输入图像描述

对nested_mod的额外更改 在此输入图像描述

现在让我们回过头来回答这个问题:我们将 blah1 和 blah2 添加到 mod_1 我们添加了一个 utils,其中包含另一个 mod 日志记录,它调用一些 fn。我们的 mod_1/mod.rs 现在包含:

pub mod blah.rs
pub mod blah2.rs
Run Code Online (Sandbox Code Playgroud)

我们创建了一个在 main 中使用的 utils/mod.rs,其中包含:

pub mod logging
Run Code Online (Sandbox Code Playgroud)

然后是一个名为logging/的目录,其中包含另一个mod.rs,我们可以将fns放入logging中以进行导入。

在此输入图像描述

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

另请参阅第 7 章中的库示例和 14.3,后者进一步扩展了 Rust Book 中工作空间的拆分。祝你好运!