模块定义

mod

模块 让我们可以将一个 crate 中的代码进行分组,以提高可读性与重用性。模块还可以控制项的 私有性,即项是可以被外部代码使用的(public),还是作为一个内部实现的内容,不能被外部代码使用(private)。

模块定义

模块通过关键字 mod 加模块定义, 例如:

mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {}
    }
}

文件 src/main.rs 和 src/lib.rs, 对应的模块是 crate, 箱(crate)的模块结构(module structure), 也叫做模块树(module tree):

crate
 └── front_of_house
     ├── hosting
        ├── add_to_waitlist
        └── seat_at_table
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment

模块 crate 默认存在, 不需要通过关键字 mod 方式来定义。与文件系统类似, 文件系统的根节点是 /, 箱(crate)的根节点是 crate.

  • 绝对路径,从箱(crate)的根节点开始, 箱(crate)的名称, 或 crate
  • 相对路径,从当前模块开始, 可以使用 self, super
// 文件内绝对路径 Absolute path
crate::front_of_house::hosting::add_to_waitlist();

// 相对路径 Relative path
front_of_house::hosting::add_to_waitlist();

同文件模块

相关代码和数据被分组到一个模块中并存储在同一文件中。

fn main() {
   greetings::hello();
}

mod greetings {
  // ⭐️ By default, everything inside a module is private
  pub fn hello() { // ⭐️ So function has to be public to access from outside
    println!("Hello, world!");
  }
}

模块也可以嵌套。

fn main() {
  phrases::greetings::hello();
}

mod phrases {
  pub mod greetings {
    pub fn hello() {
      println!("Hello, world!");
    }
  }
}

self 关键字用于引用相同的模块,而 super 关键字用于引用父模块。还可以使用 super 关键字从模块内部访问根函数。

fn main() {
  greetings::hello();
}

fn hello() {
  println!("Hello, world!");
}

mod greetings {
  pub fn hello() {
    super::hello();
  }
}

编写测试时,最好在测试模块中编写测试,因为它们仅在运行测试时才编译。

fn greet() -> String {
    "Hello, world!".to_string()
}

#[cfg(test)] // only compiles when running tests
mod tests {
    use super::greet; // import root greet function

    #[test]
    fn test_greet() {
        assert_eq!("Hello, world!", greet());
    }
}

模块可见性

  • 所有元素, 函数 functions, 方法 methods, 结构体 structs, 枚举 enum, 模块 modules, 常量 constants, 默认是私有的, 对外公开(public), 需要通过关键字pub声明
    • 即便是共有的结构体(public structs), 内部的元素(fields)和方法(methods)仍是私有的(private)
    • 共有的枚举(public enums), 其所有变天(variants)也同为共有(public)
  • 父模块中的元素, 不能使用子模块中的私有元素
  • 子模块中的元素, 可以使用父模块的元素

譬如将制作一个名为 print_things 的 mod,它具有一些与打印相关的功能。

mod print_things {
    use std::fmt::Display;

    fn prints_one_thing<T: Display>(input: T) { // Print anything that implements Display
        println!("{}", input)
    }
}

fn main() {}

可以看到我们在 print_things 里面写了std::fmt::Display;,因为它是一个单独的空间。如果您在 main()中使用std::fmt::Display;编写,将无济于事。同样,我们现在不能从main()调用它。如果在 fn 前面没有 pub 关键字,它将保持私有状态。让我们尝试在没有发布的情况下调用它。这是一种编写方式:

// 🚧
fn main() {
    crate::print_things::prints_one_thing(6);
}

crate 意味着在该文件中。里面是 mod print_things,最后是 prints_one_thing() 函数。您可以每次编写该代码,也可以编写用于导入的用法。现在,我们可以看到错误提示它是私有的:

// ⚠️
mod print_things {
    use std::fmt::Display;

    fn prints_one_thing<T: Display>(input: T) {
        println!("{}", input)
    }
}

fn main() {
    use crate::print_things::prints_one_thing;

    prints_one_thing(6);
    prints_one_thing("Trying to print a string...".to_string());
}

error[E0603]: function `prints_one_thing` is private
  --> src\main.rs:10:30
   |
10 |     use crate::print_things::prints_one_thing;
   |                              ^^^^^^^^^^^^^^^^ private function
   |
note: the function `prints_one_thing` is defined here
  --> src\main.rs:4:5
   |
4  |     fn prints_one_thing<T: Display>(input: T) {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

很容易理解函数 print_one_thing 是私有的。它还显示了在哪里可以找到该函数。这是因为一旦开始使用 mod,您可能还会使用多个文件,并且很难找到内容。现在我们只写 pub fn 而不是 fn,一切正常。

mod print_things {
    use std::fmt::Display;

    pub fn prints_one_thing<T: Display>(input: T) {
        println!("{}", input)
    }
}

fn main() {
    use crate::print_things::prints_one_thing;

    prints_one_thing(6);
    prints_one_thing("Trying to print a string...".to_string());
}

6
Trying to print a string...
上一页
下一页