在Rust中,mod.rs文件的目的是什么?

73
在一些 Rust 项目中(例如 pczarn/rustboot),我看到了目录中有 mod.rs 文件,但不知道其具体用途。我找不到相关文档,并在其他许多 Rust 项目中也看到了这种情况。 mod.rs 文件的作用是什么?何时应该使用它?
3个回答

80

想象以下目录结构:

code/
  `- main.rs
   - something/
     `- mod.rs

如果在main.rs中输入mod something;,则它将查找something/mod.rs文件作为something模块声明的内容。

另一种方法是在code/目录下有一个名为something.rs的文件。

因此,总结一下,当您编写空的模块声明(例如mod something;)时,它会查找:

  • 同一目录中名为something.rs的文件
  • 同一目录中名为mod.rs的文件夹中名为something的文件夹中的文件

然后使用这些文件之一的内容作为模块声明的内容。


1
我可以使用任何文件名代替 mod.rs 吗,还是这是内置的? - Liam Marshall
2
您可以使用#[path = "thefile.rs"]属性指定任何路径,但通常不建议这样做,因为惯例是我在答案中指定的。请参阅参考文献 - Jorge Israel Peña
2
  1. 当两个文件都存在时会发生什么?something.rssomething/mod.rs
  2. 每个文件的默认名称都是mod.rs,这不是很奇怪吗?这样会让搜索变得非常糟糕。
- Coder
1
在 Rust 2018 中,不再需要 mod.rs。https://doc.rust-lang.org/edition-guide/rust-2018/path-changes.html - Jagger Yu

76
模块是理解的重要部分,但我发现大多数文档在这方面常常让人摸不着头脑。
是来自Python还是Javascript?
大致上,`mod.rs`有点像Python中的`__init__.py`或Javascript中的`index.js`。 但只是有点像。在Rust中更加复杂一些。
Rust是不同的。
在Rust中,文件夹不能立即用作模块。
您必须在文件夹中添加一个名为`mod.rs`的文件,以公开一个名为该文件夹的新模块。 `mod.rs`中的代码是该模块的内容。 文件夹中的所有其他文件可能会被公开为子模块(下面会详细介绍)。
等等,还有另一种方法。
您可以在文件夹中添加一个名为`.rs`的文件,而不是添加`mod.rs`文件。
正如评论中的MarkusToman所指出的那样,自Rust 1.30以来,这是首选方法。
Rust参考中:
引用: 注意:在rustc 1.30之前,使用mod.rs文件是加载具有嵌套子模块的一种方式。鼓励使用新的命名约定,因为它更一致,并避免在项目中有许多名为mod.rs的文件。
完整示例:
src
    utils
        bar.rs
        foo.rs
    main.rs

此时,编译器还不知道 src/utils/foo.rssrc/utils/bar.rs 的存在。
首先,你需要暴露 src/utils/。如上所示,你有两个选项:
  • 添加文件:src/utils/mod.rs
  • 添加文件:src/utils.rs(与文件夹同名,不带扩展名)
现在,相对于 src 文件夹(也就是 crate 级别),一个名为 utils 的模块已经可用。
其次,你需要暴露文件 src/utils/foo.rssrc/utils/bar.rs
为此,utils 模块必须声明两个以这些文件命名的子模块。 因此,src/utils/mod.rs(或 src/utils.rs)的内容应为:
pub mod bar;
pub mod foo;

现在,这两个文件中公开的内容可以在其他模块中使用!
而且你可以在`src/main.rs`中编写以下内容:
mod utils;
use utils::{foo, bar};

生成的文件结构

选项1 • mod.rs(旧的方式):

src
    utils
        bar.rs
        foo.rs
        mod.rs
    main.rs

选项2 • <folder_name>.rs(首选方式):
src
    utils
        bar.rs
        foo.rs
    utils.rs
    main.rs



关于模块如何工作的更高级细节

这只是一个表面的解释,你的下一个目的地是官方文档。

有一种第三种声明模块的方式(核心语言):

mod utils {
  // module code goes here. That's right, inside of the file.
}

但也可以只写mod utils;。 在这种情况下,如上所示,Rust会搜索src/utils.rssrc/utils/mod.rs
你看,当你尝试在文件中使用一个模块(例如在src/main.rs中)时, 你可以通过以下方式引用它:
  • 从内部:src/main.rs
    • mod module { ... }
  • 从内部的嵌套模块:src/main.rs
    • mod module { pub mod sub_module { ... } }
  • 从同级文件:src/*.rs
  • 从同级文件夹中的mod.rs文件:src/*/mod.rs
  • (以及以上方式的无限递归组合)

一个包含`mod.rs`的文件或文件夹并不会成为一个模块。 相反,Rust语言允许你使用文件层次结构来组织模块(一种语言特性)。
真正有趣的是,你可以自由地将所有方法混合在一起。
例如,你可能认为你不能直接从`main.rs`中引用`src/utils/foo.rs`。 但实际上你是可以的:
// src/main.rs
mod utils {
  pub mod foo;
}

重要说明:

  • 在文件中声明的模块始终优先(因为实际上您不需要搜索文件层次结构)
  • 您不能使用其他两种方法引用相同的模块

例如,同时拥有src/utils.rssrc/utils/mod.rs将在编译时引发以下错误:

error[E0761]: file for module `utils` found at both "src/utils.rs" and "src/utils/mod.rs"
 --> src/main.rs:1:1
  |
1 | mod utils;
  | ^^^^^^^^^^
  |
  = help: delete or rename one of them to remove the ambiguity


让我们总结一下。模块对编译器是可见的:
- 从上到下 - 只能通过引用访问 (这就是为什么在导入模块之前你没有智能感知) - 从入口点开始 (默认情况下是`src/main.rs`或`src/lib.rs`。 但你可以在`Cargo.toml`中配置任何其他的入口点。 然而,这与本问题关系不大)
根据我们之前的示例,我们按顺序得到:
1. `src/main.rs` -> `crate` 因为`crate`模块包含`mod utils;`,所以我们接下来得到:
2. `src/utils.rs` 或 `src/utils/mod.rs` -> `crate::utils` 因为`utils`模块包含`mod foo;`,所以我们接下来得到:
3. `src/utils/foo.rs` -> `crate::utils::foo`

5
谢谢,这方面《Rust 书》确实有些薄弱。当你暴露出foo和bar后,你会使用utils::foo还是直接使用foo呢?我猜想是后者? - Markus Toman
2
我刚在https://doc.rust-lang.org/reference/items/modules.html#module-source-filenames中发现:“注意:在rustc 1.30之前,使用mod.rs文件是加载具有嵌套子项的模块的方法。鼓励使用新的命名约定,因为它更一致,并避免在项目中有许多名为mod.rs的文件。” - Markus Toman
@MarkusToman 要在文件(例如main.rs)中使用foo和bar,您需要使用mod utils;将模块引入范围,然后使用use utils::{foo, bar}; - Romain Vincent
你的解释有一个问题。通过在文件夹中添加mod.rs,文件夹就会变成模块,因此您不需要在mod.rs中导出与文件夹同名的模块,只需导出子模块即可。文件夹会自动变成一个模块。mod.rs是一种避免创建具有相同名称(+.rs)的文件夹和文件以拥有嵌套模块的方法。 - Paul-Sebastian Manole
1
@TalnaciSergiuVlad 非常愉快。模块总是让人头疼,就像你一样,我也对所有官方文档和示例感到有些失望。 - undefined
显示剩余5条评论

1

除了lib.rs和main.rs总是匹配crate模块,每个rs文件都会有自己的模块。

声明模块的方法只有一种:

/* pub */ mod sub_module1;

一个模块不能在根/包模块树之外声明(即,向上遍历模块树,子模块必须始终具有直接在 lib.rsmain.rs 中声明的父模块,因此第一个程序子模块必须始终在那里声明 - 如果这还不够明显,就像一棵树形数据结构)。

有两种方法可以将模块嵌套在其声明所在的模块中:

  • <module_where_it_is_declared>/<module_name.rs>
  • <module_where_it_is_declared>/module_name/mod.rs

如果 module_where_it_is_declared 是包模块,则不需要相应的子文件夹(从上面的方案中消失)。

以下是适用于 lib 和 binary crates 的示例:

src
|---lib.rs ( contains: pub mod b2; )
|---b2.rs ( contains: pub mod bb2; )
|---b2
|   |---bb2.rs
.   .

另外:

src
|---lib.rs ( contains: pub mod b2; )
|---b2
|   |---mod.rs ( contains: pub mod bb2; )
|   |---bb2.rs
.   .

您可以看到,您可以混合使用(b2使用mod.rs方式,bb2使用“文件”方式)。

以下是仅使用文件模式的有效方法:

src
|---lib.rs ( contains: pub mod b2; )
|---b2.rs ( contains: pub mod bb2; )
|---b2
|   |---bb2.rs (contains: pub mod bbb2; )
|   |---bbb2.rs (contains: pub mod bbbb2; )
|   |---bbb2
|   |   |---bbbb2.rs
.   .   .

我想这取决于您如何嵌套模块。

对于仅导出其他子模块并且没有其他(或非常少)代码的模块,我喜欢使用mod.rs语法,尽管您可以在mod.rs中放置任何内容。

我使用mod.rs类似于JS/TS世界中的桶模式,将几个子模块卷起来成为一个单独的父模块。

此外,请不要忘记模块可以通过添加作用域块进行内联定义(而不仅仅是声明):

pub mod my_submodule {
    // ...
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接