是否可以避免使用"mod.rs"文件



我阅读了 mod 文档,发现我必须在每个文件夹中添加mod.rs,例如,我必须在项目结构中像这样定义 mod:

src/
models/
login.rs
mod.rs
routes/
login_route.rs
mod.rs
services/
login_service.rs
mod.rs
main.rs

并在main.rs中像这样使用它:

mod models;
mod routes;
mod services;

为什么要这样做?为什么要这样设计?如果项目增加,该项目有很多mod.rs文件只是为了暴露 mod?这是一种好的做法吗?我不明白。这样做有什么好处?如果我们只是这样做:

crate::models::login;

它是如此清晰易懂。

其中一些在 mod.rs 文件的用途是什么?

本质上,Rust 不会对文件结构做出任何假设,也不会在没有开发人员声明的情况下考虑其他.rs文件。相反,他们打算使用模块系统在其代码中构建组织结构。

虽然这与其他一些语言不同,但这为开发人员提供了更多的控制权。由于导入路径与文件结构分离,因此可以根据需要重新组织和重新导出模块、结构、函数等。这也允许您准确声明编译的内容,这有助于条件编译。

那么 Rust 什么时候使用文件结构呢?当您声明没有主体的模块时:

mod models;

它将查找要用于该模块的文件。

  • 从普通文件(例如utils.rs)中,它将查找嵌套文件/目录:

    ./utils/models.rs
    ./utils/models/mod.rs
    
  • mod.rs或顶级lib.rsmain.rs(它们在这方面很特别)中,它将查找一个同级文件/目录:

    ./models.rs
    ./models/mod.rs
    

从概念上讲,mod.rs的使用允许您像使用文件一样使用目录(如果您熟悉 Javascript,它类似于index.js)。


有没有办法避免所有这些mod.rs文件?

有两种不错的选择:

  1. 使用models.rs而不是models/mod.rs

    只需将mod.rs文件上移一级,然后重命名它们以匹配其目录即可。您不必修改main.rs或任何文件的内容。您的结构可能如下所示:

    src/
    models/
    login.rs
    routes/
    login_route.rs
    services/
    login_service.rs
    main.rs
    models.rs
    routes.rs
    services.rs
    

    这正在慢慢成为首选方法[需要引用],因为它为文件提供了更具描述性的名称。

  2. 只需在main.rs中声明您的模块结构:

    这有点不合常规,但嵌套模块声明将找到嵌套文件。您在任何地方都不需要mod.rs

    src/
    models/
    login.rs
    routes/
    login_route.rs
    services/
    login_service.rs
    main.rs
    

    如果像这样在main.rs中声明模块:

    mod models {
    mod login; // this will use ./models/login.rs
    }
    mod routes {
    mod login_route; // this will use ./routes/login_route.rs
    }
    mod services {
    mod login_service; // this will use ./services/login_service.rs
    }
    

    这不是特别推荐的。它可能在小型项目中工作正常,但随着代码库变大,它会变得笨拙。您可能希望使用上面显示的mod.rs或方法,以保持代码的编写。


最后,这些传递模块不仅用于声明其他文件。它们是一个方便的地方:

  • 将共享代码放在其子模块中
  • 包括有关如何设计和/或打算使用其所包含的结构、特征等
  • 的文档
  • 重新导出深度嵌套的项,以便代码的其余部分更容易访问它们

总的来说,模块系统只是另一个抽象级别,以保持代码的良好封装。

从 Rust 2018 版开始,mod.rs文件是可选的:

在 Rust 2015 中,如果你有一个子模块:

// This `mod` declaration looks for the `foo` module in
// `foo.rs` or `foo/mod.rs`.
mod foo;

它可以生活在foo.rsfoo/mod.rs.如果它有自己的子模块,则必须foo/mod.rs.所以 foo 的酒吧子模块将存在于foo/bar.rs.

在 Rust 2018 中,取消了具有子模块的模块必须命名mod.rs的限制。foo.rs可以foo.rs,子模块还是foo/bar.rs。这消除了特殊名称,如果您在编辑器中打开了一堆文件,则可以清楚地看到它们的名称,而不是具有一堆名为mod.rs的选项卡。

其他资源:

  • 鲁斯特的参考资料
  • 版本指南

添加路径属性

就像您在问题中提到的一样,但带有一个小注释。此示例应说明如何执行此操作:

使用与 OP 问题中相同的目录结构:

src/
models/
login.rs
routes/
login_route.rs
services/
login_service.rs
main.rs

main.rs文件可以按如下方式声明模块:

#[path = "models/login.rs"]
mod models;
#[path = "routes/login_route.rs"]
mod routes;
#[path = "services/login_service.rs"]
mod services;

这种方法的优点是您的mod <module_name>不需要与原始文件或目录相同 - 允许在需要时使用较短的名称。

但是,缺点是这将无法引用子目录中的多个模块。(例如,如果models/目录中有更多模块,则会出现问题。

问题似乎已经进一步发展到"是否有可能同时避免mod.rs<directory>.rs"或"如果两者都不存在,但./<directory>(或./<directory>/<file>)存在,为什么不mod name;视为与@allidoiswin提到的空./<directory>/mod.rs.一样"。

这样的文件可能是不可避免的。这可能是因为目录仅指示模块树中存在节点,而不描述节点。但是,.rs文件中的mod可以完全描述节点的信息(例如 mod 中的函数、子模块的可见性)。

假设我们要将main.rs中的mod house移动到目录中。

.
└── main.rs
// main.rs
mod house {
mod guest {}
pub mod host { 
pub fn clean() {
super::clean_house();
}
}
fn clean_house() {
println!("Cleaned!");
}
}

所以我们以这种方式制作目录,并希望避免house.rs.

.
├── house
│   ├── guest.rs
│   └── host.rs
└── main.rs
// main.rs
mod house;
fn main() {
house::host::clean();
}
// host.rs
pub fn clean() {
super::clean_house();
}

但是我们找不到在哪里编写clean_house()函数并给mod host可见性,因为house是一个目录而不是一个 rust 代码文件。

解决方案是添加一个house.rs文件,该文件提供了目录house的额外信息。

.
├── house
│   ├── guest.rs
│   └── host.rs
├── house.rs
└── main.rs
// house.rs
mod guest;
pub mod host;
fn clean_house() {
println!("Cleaned!");
}

如果我们认为househouse.rs是共同活动的,并且house目录是存储house.rs子模块的地方,那么可能会有一些一致性。

最新更新