rust 将模块拆分为多个文件

cl25kdpy  于 2023-01-13  发布在  其他
关注(0)|答案(7)|浏览(136)

我希望有一个模块包含多个struct,* 每个struct都在自己的文件中。* 以Math模块为例:

Math/
  Vector.rs
  Matrix.rs
  Complex.rs

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

use Math::Vector;

fn main() {
  // ...
}

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

watbbzwu

watbbzwu1#

Rust的模块系统实际上非常灵活,可以让你公开任何你想要的结构,同时隐藏你的代码在文件中的结构。
我认为这里的关键是使用pub use,它允许您从其他模块重新导出标识符,在Rust的std::io机箱中有这样的先例,其中子模块中的一些类型被重新导出以在std::io中使用。

编辑(2019年8月25日):答案的以下部分是很久以前写的。它解释了如何单独使用rustc设置这样一个模块结构。今天,人们通常会使用Cargo来处理大多数用例。虽然以下部分仍然有效,但它的某些部分(例如#![crate_type = ...])可能看起来很奇怪。这不是推荐的解决方案。

为了适应您的示例,我们可以从以下目录结构开始:

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![] }
    }
}

从客户端的Angular 来看,VectorAVectorB是在两个不同的模块和两个不同的文件中定义的,这是完全不透明的。
如果您与main.rs位于同一个目录中,您应该能够使用以下命令运行它:

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

总的来说,Rust书中的X11E1F1X还是相当不错的,有很多例子。
最后,Rust编译器还会自动查找子目录。例如,上面的代码将在以下目录结构中工作:

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

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

3vpjnl9f

3vpjnl9f2#

Rust模块规则包括:
1.源文件只是它自己的模块(特殊文件main.rs、lib.rs和mod.rs除外)。
1.目录只是模块路径组件。
1.文件 mod.rs只是目录的模块。
math目录下的matrix.rs1文件就是math::matrix模块,这很简单,你在文件系统上看到的东西,也可以在源代码中找到,这是文件路径和模块路径的一一对应关系。
所以你可以用use math::matrix::Matrix导入一个结构体Matrix,因为这个结构体在matrix.rsmath目录下的www.example.com文件中。不满意吗?你更喜欢use math::Matrix;,不是吗?这是可能的。用以下命令重新导出math/ www.example.com中的标识符x1m4 n1mod.rsx:

pub use self::math::Matrix;

还有一个步骤可以让它工作。Rust需要一个模块声明来load模块。在main.rs中添加一个mod math;。如果你不这样做,在导入时你会从编译器中得到一个错误消息,如下所示:

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

这里的提示是误导性的。没有必要使用额外的板条箱,除非您确实打算编写一个单独的库。
将此添加到www.example.com的顶部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;

你就完了。
1源文件名在Rust中通常以小写字母开头,这就是我使用matrix.rs而不是Matrix.rs的原因。
2 Java不同。你用package声明路径,也是。这是多余的。路径已经从源文件在文件系统中的位置明显。为什么要在文件顶部的声明中重复这些信息呢?当然,有时快速浏览源代码比找出文件的文件系统位置更容易。我能理解人们说'It“没那么令人困惑。

6kkfgxo0

6kkfgxo03#

Rusts纯粹主义者可能会称我为异教徒,并讨厌这个解决方案,但这要简单得多:只需在其自己的文件中执行每项操作,然后使用www.example.com中的“include!“宏mod.rs:

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

这样你就不会得到额外的嵌套模块,避免了复杂的导出和重写规则。简单,有效,没有麻烦。

n8ghc7c1

n8ghc7c14#

好吧,我和编译器斗争了一段时间,终于让它工作了(感谢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。

xmq68pz9

xmq68pz95#

我想在这里添加如何包括Rust文件时,他们是深嵌套。我有以下结构:

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

如何从main.rs访问sink.rstoilet.rs
正如其他人所提到的,Rust不知道文件。相反,它把所有东西都看作模块和子模块。要访问浴室目录中的文件,你需要导出它们或把它们放在顶部。你可以通过指定一个文件名和你想访问的目录以及文件中的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.在home目录中创建名为bathroom.rs的文件:
1.导出文件名:

// bathroom.rs
pub mod sink;
pub mod toilet;

1.在main.rs旁边创建名为home.rs的文件

  1. pub mod www.example.com文件 bathroom.rs file
// home.rs
pub mod bathroom;

1.在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();
}

在子模块中包括其他兄弟模块(文件)

如果您希望使用toilet.rs中的sink.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!
换句话说,在toilet.rs中执行pub mod sink;use self::sink;将 * 不起作用 *,除非您一直将它们暴露到main.rs!
因此,永远记得桶你的文件到顶部!

hmae6n7t

hmae6n7t6#

导出模块的一个更简单的方法,我从Github中学到的。

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

    mod a;
    mod b;

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

1cklez4t7#

调整问题的示例目录和文件名以符合Rust命名约定:

main.rs
math.rs
math/
  vector.rs
  matrix.rs
  complex.rs

确保导出math目录下每个文件中的公共符号(类型、函数等),方法是在它们前面加上关键字pub
定义math.rs

mod vector;
pub use vector::*;

mod matrix;
pub use matrix::*;

mod complex;
pub use complex::*;

上面的文件保持math的子模块私有,但是子模块的公共符号从模块math导出,这有效地扁平化了模块结构。
main.rs中使用math::Vector

mod math;

use crate::math::Vector;

fn main() {
  // ...
}

相关问题