将模块分成几个文件
我想要一个有多个结构的模块, 每个模块都有自己的文件。 以Math
模块为例:
Math/ Vector.rs Matrix.rs Complex.rs
我希望每个结构都在同一个模块中,我将使用我的主文件,如下所示:
use Math::Vector; fn main() { // ... }
然而,Rust的模块系统(开始时有点混乱)没有提供一个明显的方法来做到这一点。 它似乎只允许你在一个文件中有你的整个模块。 这是不是土气? 如果没有,我该怎么做?
Rust的模块系统实际上非常灵活,可以让你公开任何你想要的结构,同时隐藏你的代码在文件中的结构。
我认为这里的关键是利用pub use
,这将允许您从其他模块重新导出标识符。 在Rust的std::io
crate中有这样的先例,其中来自子模块的一些types被重新导出用于std::io
。
为了适应你的例子,我们可以从这个目录结构开始:
src/ lib.rs vector.rs main.rs
这是你的main.rs
:
extern crate math; use math::vector; fn main() { println!("{:?}", vector::VectorA::new()); println!("{:?}", vector::VectorB::new()); }
和你的src/lib.rs
:
#[crate_id = "math"]; #[crate_type = "lib"]; pub mod vector; // exports the module defined in vector.rs
最后, src/vector.rs
:
// exports identifiers from private sub-modules in the current // module namespace pub use self::vector_a::VectorA; pub use self::vector_b::VectorB; mod vector_b; // private sub-module defined in vector_b.rs mod vector_a { // private sub-module defined in place #[derive(Debug)] pub struct VectorA { xs: Vec<i64>, } impl VectorA { pub fn new() -> VectorA { VectorA { xs: vec![] } } } }
这就是魔术发生的地方。 我们已经定义了一个子模块math::vector::vector_a
,它有一些特殊types的vector的实现。 但是我们不希望你的库的客户端在意有一个vector_a
子模块。 相反,我们想使它在math::vector
模块中可用。 这是通过pub use self::vector_a::VectorA
,它在当前模块中重新导出vector_a::VectorA
标识符。
但是你问如何做到这一点,以便你可以把你的特殊的vector实现在不同的文件。 这是mod vector_b;
行呢。 它指示Rust编译器为该模块的实现寻找一个vector_b.rs
文件。 果然,这是我们的src/vector_b.rs
文件:
#[derive(Debug)] pub struct VectorB { xs: Vec<i64>, } impl VectorB { pub fn new() -> VectorB { VectorB { xs: vec![] } } }
从客户的angular度来看, VectorA
和VectorB
在两个不同的文件中定义在两个不同的模块中的事实是完全不透明的。
如果你在main.rs
所在的目录下,你应该可以运行它:
rustc src/lib.rs rustc -L . main.rs ./main
一般来说,Rust书中的“Crates and Modules”一章非常好。 有很多例子。
最后,Rust编译器还会自动查找子目录。 例如,上面的代码将在这个目录结构下工作:
src/ lib.rs vector/ mod.rs vector_b.rs main.rs
编译和运行的命令也保持不变。
Rust模块的规则是:
- 源文件只是它自己的模块(除了特殊文件main.rs,lib.rs和mod.rs)。
- 目录只是一个模块path组件。
- 文件mod.rs 只是目录的模块。
目录math中的文件matrix.rs 1 只是模块math::matrix
。 这很容易。 您在文件系统上看到的内容也可以在源代码中find。 这是文件path和模块path2的一一对应关系。
所以你可以use math::matrix::Matrix
来导入一个结构Matrix
,因为这个结构在一个目录math的文件matrix.rs里面。 不开心? 你更喜欢use math::Matrix;
非常多,不是吗? 这是可能的。 在math / mod.rs中使用以下命令重新导出标识符math::matrix::Matrix
:
pub use self::math::Matrix;
还有一个步骤来实现这个工作。 Rust需要一个模块声明来加载模块。 添加一个mod math;
在main.rs. 如果你不这样做的话,当你像这样导入的时候,你会得到一个来自编译器的错误消息:
error: unresolved import `math::Matrix`. Maybe a missing `extern crate math`?
这个提示在这里是误导。 除了当然你打算写一个单独的图书馆,不需要额外的箱子。
添加这个在main.rs的顶部:
mod math; pub use math::Matrix;
模块声明也是子模块vector
, matrix
和complex
必需的,因为math
需要加载它们来重新导出它们。 如果您已加载标识符的模块,则只能重新导出标识符。 这意味着,要重新导出标识符math::matrix::Matrix
您需要编写mod matrix;
。 你可以在math/ mod.rs中做到这一点。 因此用这个内容创build文件:
mod vector; pub use self::vector::Vector; mod matrix; pub use self::matrix::Matrix; mod complex; pub use self::complex::Complex;
Aaa,你完成了。
1源文件名通常在Rust中以小写字母开头。 这就是为什么我使用matrix.rs而不是Matrix.rs。
2 Java的不同。 你也用package
声明path。 这是多余的。 从文件系统中的源文件位置已经可以看出path。 为什么要在文件顶部的声明中重复这些信息? 当然,有时快速查看源代码更容易,而不是查找文件的文件系统位置。 我可以理解那些说不那么困惑的人。
好吧,我的编译器打了一阵子,终于得到它的工作(感谢BurntSushi指出pub use
。
main.rs:
use math::Vec2; mod math; fn main() { let a = Vec2{x: 10.0, y: 10.0}; let b = Vec2{x: 20.0, y: 20.0}; }
math/ mod.rs:
pub use self::vector::Vec2; mod vector;
math/ vector.rs
use std::num::sqrt; pub struct Vec2 { x: f64, y: f64 } impl Vec2 { pub fn len(&self) -> f64 { sqrt(self.x * self.x + self.y * self.y) } // other methods... }
其他结构可以以相同的方式添加。 注:用0.9编译,不是主。
Rusts纯粹主义者可能会称我为异端,讨厌这个解决scheme,但这更简单:只要在自己的文件中做每件事,然后在mod.rs中使用“ include! ”macros:
include!("math/Matrix.rs"); include!("math/Vector.rs"); include!("math/Complex.rs");
这样你就不会添加嵌套的模块,并避免复杂的导出和重写规则。 简单,有效,没有大惊小怪。