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

dy1byipe  于 2023-03-02  发布在  其他
关注(0)|答案(5)|浏览(162)

如果目录结构如下所示:

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

如何使用其他文件中的函数?
从Rust教程中,听起来我应该能够做到这一点:

主文件.rs

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

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

日志记录.rs

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

废话

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

blah2.rs

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

但是,这会产生一个错误:

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"

看起来,沿着路径向下导入(即从mainmodule1/blah.rs)是有效的,导入对等体(即从blahblah2)是有效的,但从父作用域导入则无效。
如果我使用神奇的#[path]指令,就可以实现以下功能:

blah2.rs

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

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

我真的必须手动使用相对文件路径从父作用域级别导入一些东西吗?在Rust中没有更好的方法来做到这一点吗?
在Python中,我们使用from .blah import x作为局部作用域,但是如果你想访问一个绝对路径,你可以使用from project.namespace.blah import x

l7mqbcuq

l7mqbcuq1#

我也要回答这个问题,对于任何其他人谁发现这一点,是(像我一样)完全困惑的难以理解的答案。
它归结为两件事,我觉得在教程中解释得很差:

  • mod blah;语法为编译器导入一个文件。你**必须在所有你想编译的文件上使用这个语法。
  • 与共享库一样,可以使用use blah::blah;将定义的任何本地模块导入到当前作用域中。

一个典型的例子是:

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

在这种情况下,可以使用usetwo.rs中的代码放入one.rs中:

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

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

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

请注意,您可以使用blah/mod.rs文件来稍微减轻这种尴尬,方法是放置类似one/mod.rs的文件,因为mod x;尝试将x.rsx/mod.rs作为加载。

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

您可以将www.example.com顶部笨拙的文件导入减少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();
}

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

irtuqstp

irtuqstp2#

我假设您希望在顶层声明utilsutils::logging,并且只希望在module1::blah::blah2中从它们调用函数。模块的声明是用mod完成的,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();
}

我做的唯一更改是blah2中的use crate::utils;行(在Rust 2015中,您还可以使用use utilsuse ::utils)。有关use如何工作的更多详细信息,请参见此答案的后半部分。relevant section of The Rust Programming Language也是一个合理的参考,特别是以下两个小节:

另外,请注意,我将其全部内联编写,将foo/bar.rs的内容直接放置在mod foo { mod bar { <contents> } }中,将其更改为mod foo { mod bar; },相关文件应该是相同的。
(By println(": {}\n", msg)打印两个新行;println!已经包含一个(ln是“行”),print!(": {}\n", msg)println!(": {}", msg)只打印一个。)
要得到您想要的确切结构并不符合惯例,您必须对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();
}

实用程序/logging.rs

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

第1单元/blah.rs

mod blah2;

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

module 1/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");
}
bvhaajcl

bvhaajcl3#

我知道这是一个很老的职位,可能没有使用2018年。但是,这仍然是真的很棘手,我想帮助那些正在寻找。
因为图片胜过千言万语,所以我简化了代码分割。

然后,正如您可能猜到的,它们都有一个空的pub fn some_function()。
我们可以通过对主

的更改来进一步扩展这一点

nested_mod x1c4d 1x的其他更改
现在让我们回过头来回答这个问题:我们在mod_1中添加了blah 1和blah 2。我们添加了一个utils,其中包含另一个mod日志,调用了一些fn。我们的mod_1/mod.rs现在包含:

pub mod blah.rs
pub mod blah2.rs

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

pub mod logging

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

源代码也在此处https://github.com/DavidWhit/Rust_Modules
另外,请查看第7章中的库示例,以及Rust Book中进一步扩展了工作空间拆分的14.3。祝你好运!

vq8itlhq

vq8itlhq4#

这里的答案对我来说并不清楚,所以我将把我的两分钱给未来的 rust 虫。
您需要做的就是通过src/main.rssrc/main.rs中声明所有文件(当然还有fn main)。

// src/main.rs

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

fn main () {
    module1::blah::doit();
}

通过pub公开所有你需要在外部使用的东西(复制粘贴原始的src/utils/logging.rs),然后通过crate::简单地声明use的模块。

// src/module1/blah.rs

use crate::utils::logging;
// or `use crate::utils` and `utils::logging::("log")`, however you like

pub fn doit() {
  logging::trace("Logging works");
}

crate::看作~/,其中~是cargo项目的src文件夹,还有一个关键字super::可以从当前目录访问(如./)。
为了得到一个更清晰的答案,我稍微打乱了一些函数。

3yhwsihp

3yhwsihp5#

如果你创建了一个名为mod.rs的文件,rustc在导入模块时会查看它,我建议你创建一个文件src/utils/mod.rs,并使其内容如下所示:

pub mod logging;

然后,在main.rs中添加如下语句:

use utils::logging;

然后用

logging::trace(...);

或者你可以

use utils::logging::trace;

...

trace(...);

基本上,在mod.rs文件中声明模块,在源文件中声明use

相关问题