将模块拆分为多个文件

时间:2014-03-23 20:53:08

标签: module rust

我想要一个包含多个结构的模块,每个都在自己的文件中。使用Math模块作为示例:

Math/
  Vector.rs
  Matrix.rs
  Complex.rs

我希望每个结构都在同一个模块中,我将从我的主文件中使用它,如下所示:

use Math::Vector;

fn main() {
  // ...
}

然而,Rust的模块系统(开始时有点混乱)并没有提供一种明显的方法来实现这一点。它似乎只允许您将整个模块放在一个文件中。这不是质朴的吗?如果没有,我该怎么做?

6 个答案:

答案 0 :(得分:91)

Rust的模块系统实际上非常灵活,可以让你暴露出你想要的任何类型的结构,同时隐藏你的代码在文件中的结构。

我认为这里的关键是使用pub use,这将允许您从其他模块重新导出标识符。在Rust的std::io箱子中有先例,其中子模块的某些类型是re-exported for use in 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,它有一些特殊类型的向量的实现。但我们并不希望您图书馆的客户关心有vector_a个子模块。相反,我们希望在math::vector模块中提供它。这是通过pub use self::vector_a::VectorA完成的,它会在当前模块中重新导出vector_a::VectorA标识符。

但是你问如何做到这一点,以便你可以将你的特殊矢量实现放在不同的文件中。这就是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![] }
    }
}

从客户的角度来看,VectorAVectorB在两个不同文件中的两个不同模块中定义的事实是完全不透明的。

如果您与main.rs位于同一目录中,则应该可以使用以下命令运行它:

rustc src/lib.rs
rustc -L . main.rs
./main

一般来说,Rust书中的"Crates and Modules" chapter非常好。有很多例子。

最后,Rust编译器还会自动查找子目录。例如,上面的代码将在此目录结构中保持不变:

src/
  lib.rs
  vector/
      mod.rs
      vector_b.rs
main.rs

编译和运行的命令也保持不变。

答案 1 :(得分:25)

Rust模块规则是:

  1. 源文件只是自己的模块(特殊文件main.rs,lib.rs和mod.rs除外)。
  2. 目录只是模块路径组件。
  3. 文件mod.rs 只是目录的模块。
  4. 目录math 中的文件matrix.rs 1 只是模块math::matrix。这很容易。您在文件系统中看到的内容也可以在源代码中找到。这是文件路径和模块路径 2 的一对一对应关系。

    因此,您可以使用Matrix导入结构use math::matrix::Matrix,因为结构位于目录math中的文件matrix.rs内。不开心?你非常喜欢use math::Matrix;,不是吗?这是可能的。使用以下代码重新导出math / mod.rs中的标识符math::matrix::Matrix

    pub use self::math::Matrix;
    

    要实现这一目标还有另一个步骤。 Rust需要一个模块声明来加载模块。在main.rs中添加mod math;。如果您不这样做,则在导入时会从编译器收到错误消息:

    error: unresolved import `math::Matrix`. Maybe a missing `extern crate math`?
    

    提示在这里有误导性。除了你真的打算写一个单独的库之外,不需要额外的板条箱。

    在main.rs的顶部添加:

    mod math;
    pub use math::Matrix;
    

    子模块vectormatrixcomplex也需要模块声明,因为math需要加载它们才能重新导出它们。只有在加载了标识符的模块后,才能重新导出标识符。这意味着,要重新导出标识符math::matrix::Matrix,您需要编写mod matrix;。你可以在math / mod.rs中做到这一点。因此,使用以下内容创建文件:

    mod vector;
    pub use self::vector::Vector;
    
    mod matrix;
    pub use self::matrix::Matrix;
    
    mod complex;
    pub use self::complex::Complex;
    

    Aaaand你已经完成了。

    1 源文件名通常以Rust中的小写字母开头。这就是我使用matrix.rs而不是Matrix.rs的原因。

    2 Java与众不同。您也可以使用package声明路径。这是多余的。从文件系统中的源文件位置开始,路径已经很明显。为什么要在文件顶部的声明中重复此信息?当然,有时可以更快地查看源代码而不是查找文件的文件系统位置。我能理解那些说它不那么令人困惑的人。

答案 2 :(得分:15)

好吧,和我的编译器争了一段时间,最后让它工作了(感谢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};
}

数学/ mod.rs:

pub use self::vector::Vec2;
mod vector;

数学/ 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编译,而不是master。

答案 3 :(得分:13)

Rusts纯粹主义者可能会称我为异教徒并讨厌这个解决方案,但这更简单:只需在自己的文件中执行每个操作,然后使用&#34; include!&#34; mod.rs中的宏:

include!("math/Matrix.rs");
include!("math/Vector.rs");
include!("math/Complex.rs");

这样您就不会添加嵌套模块,并避免复杂的导出和重写规则。 简单,有效,没有大惊小怪。

答案 4 :(得分:5)

我想在这里添加当深度嵌套时如何包含Rust文件。我有以下结构:

|-----main.rs
|-----home/
|---------bathroom/
|-----------------sink.rs
|-----------------toilet.rs

如何从sink.rs访问toilet.rsmain.rs

正如其他人所提到的,Rust不了解文件。相反,它将所有内容视为模块和子模块。要访问bathroom目录中的文件,您需要导出它们或将它们存储在顶部。为此,您可以通过指定文件名和要访问的目录,并在文件内pub mod filename_inside_the_dir_without_rs_ext来实现。

示例。

// sink.rs
pub fn run() { 
    println!("Wash my hands for 20 secs!");
}

// toilet.rs
pub fn run() {
    println!("Ahhh... This is sooo relaxing.")
}
  1. bathroom.rs目录中创建一个名为home的文件:

  2. 导出文件名:

    // bathroom.rs
    pub mod sink;
    pub mod toilet;
    
  3. home.rs旁边创建一个名为main.rs的文件

  4. pub mod bathroom.rs文件

    // home.rs
    pub mod bathroom;
    
  5. main.rs

    // main.rs
    // Note: If you mod something, you just specify the 
    // topmost module, in this case, home. 
    mod home;
    
    fn main() {
        home::bathroom::sink::run();
    }
    

    use语句也可以使用:

    // main.rs
    // Note: If you mod something, you just specify the 
    // topmost module, in this case, home. 
    use home::bathroom::{sink, toilet};
    
    fn main() {
        sink::run();
        sink::toilet();
    }
    

在子模块中包括其他同级模块(文件)

如果您要使用sink.rs中的toilet.rs,则可以通过指定selfsuper关键字来调用模块。

// inside toilet.rs
use self::sink;
pub fn run() {
  sink::run();
  println!("Ahhh... This is sooo relaxing.")
}

最终目录结构

您最终会得到这样的东西:

|-----main.rs
|-----home.rs
|-----home/
|---------bathroom.rs
|---------bathroom/
|-----------------sink.rs
|-----------------toilet.rs

以上结构仅适用于Rust 2018及更高版本。以下目录结构对2018年也有效,但是这是2015年的工作方式。

|-----main.rs
|-----home/
|---------mod.rs
|---------bathroom/
|-----------------mod.rs
|-----------------sink.rs
|-----------------toilet.rs

其中home/mod.rs./home.rs相同,而home/bathroom/mod.rshome/bathroom.rs相同。 Rust进行了此更改,因为如果包含与目录名称相同的文件,编译器会感到困惑。 2018版(最先显示的版本)修复了该结构。

有关更多信息,请参见this repo,有关整体说明,请参见此YouTube video

最后一件事...避免连字符!请改用snake_case

重要提示

必须将所有文件存储到顶部,即使顶层文件不需要深文件也是如此。

这意味着,要让sink.rs发现toilet.rs,您需要使用上述方法对它们进行桶化,直到main.rs

换句话说,在pub mod sink;内进行use self::sink;toilet.rs不起作用,除非您一直将它们暴露到main.rs

因此,请始终记住将文件存储在顶部!

答案 5 :(得分:1)

一种更粗暴的导出模块的方法,这是我从 Github 中获取的。

mod foo {
    //! inner docstring comment 1
    //! inner docstring comment 2

    mod a;
    mod b;

    pub use a::*;
    pub use b::*;
}