前端学Rust - 模块

245 阅读4分钟

Package 和 Crate

Crate

src/main.rs

  • binary crate 的 crate 根
  • crate 名称与 package 名称相同

src/lib.rs

  • package 包含一个 library crate
  • library crate 的 crate 根
  • crate 名称与 package 名称相同

cargo 会把 crate root 文件交个rustc 来构建library或者binary文件

由此可得出结论:

  • 一个Package了可以同时包含src/main.rs和src/lib.rs,即为一个binary crate一个library crate,两个crate名称都与package名相同
  • 一个Package可以有多个binary crate:放在src/bin下面的文件每一个都是单独的binary crate

Crate作用

  1. 将相关功能组合到一个作用域内,便于在项目之间进行共享
  2. 防止命名冲突

定义Module Module

  • 在一个Crate中将代码进行分组
  • 增加可读性,易于复用
  • 控制属性的public、private

定义一个mod: mod关键字 可嵌套 可包含其他内容(struct、enum、常量、trait、函数)的定义,示例:

mod front_of_house {
  mod hosting{
    fn add_to_waitlist(){}
    fn seat_at_table(){}
  }
  mod serving {
    fn takeorder(){}
    fn server_order(){}
    fn take_payment(){}
  }
}

src/main.rs 和 src/lib.rs 叫做 create roots, 这两个文件(任意一个)的内容形成了名为crate的模块,位于整个模块树的根部

路径

绝对路径:从crate root开始,使用crate名 或 字面值crate 相对路径: 从当前模块开始,使用self、super(上一级)或当前模块的标识符 路径至少有一个标识符组成,标识符之间用 :: 分割,示例:

// src/lin.rs 
mod front_of_house {
  mod hosting{
    fn add_to_waitlist(){}
  }
}
pub fn eat_at_restaurant(){
  crate::front_of_house::hosting::add_to_waitlist();
  // front_of_house::hosting::hosting() // 相对路劲写法,因为是同一级的可以直接引用
}

报错了,why???

私有边界

  • 模块不仅可以组织代码,还可以定义私有边界
  • 如果想把函数 或者 struct 等设置为私有,可以将它直接放到某个模块中
  • Ruts中的所有条目默认都是私有的
  • 父级模块无法访问子模块中的私有条目
  • 子模块里可以使用所有祖先模块中的条目

Rust希望默认细节是隐藏的,这样可以知道修改那些代码不会破坏外部的内容。(想用必须得声明好,这就很rust了)

回到刚刚的报错:error[E0603]: module hosting is private , 如何解决这个报错呢?

关键字

pub

pub: 将条目标记为公共的,它就可以解决上述报错了,示例:

mod front_of_house {
  pub mod hosting{
    pub fn add_to_waitlist(){}
  }
}
pub fn eat_at_restaurant(){
  crate::front_of_house::hosting::add_to_waitlist();
  //   front_of_house::hosting::hosting() // 相对路劲,因为是同一级的可以直接引用
}

将pub放在struct前:

  • struct是公共的
  • struct里的字段默认是私有的,需要单独设置pub来变成公有
mod back_of_house{
  pub struct Breakfast {
    pub toast: String,
    seasonal_fruit: String,
  }

  impl Breakfast {
    pub fn summery(toast: &str) -> Breakfast {
      Breakfast {
        toast: String::from(toast),
        seasonal_fruit: String::from("peaches")
      }
    }
  }
}
pub fn eat_at_restaurant(){
  let mut meal = back_of_house::Breakfast::summery("Rye");
  meal.toast = String::from("Wheat");
  println!("I'd like {} toast please",meal.toast);
//   meal.seasonal_fruit = String::from("blueberries")  // 因为seasonal_fruit没有定义pub,所以这里如何打开则编译报错
}

pub 在 enum 前: 枚举和枚举里的所有变体都是公共的

super

super: 用来访问父级模块中的内容,类似文件系统的 .. 示例:

// src/lin.rs 
fn serve_order(){}

mod back_of_house{
  fn cook_order(){
    println!("cook_order")
  }
  fn fix_incorrect_order(){
    cook_order();
    // crate::serve_order()
    super::serve_order() // 这里和上面注释掉的一行是一模一样的
  }
}

use

可以使用use关键字将路径导入到作用域内

  • 扔遵循私有性规则 示例:

use的习惯用法

  • 针对函数,通常去引用父级模块,以便于知道是是哪里的函数
  • struct、enmu等,导入通常指定完整路径(到它本身)
  • 但是不同模块的同名条目,引用还是要指定到父级模块 示例:
// src/lin.rs 
mod front_of_house {
  pub mod hosting{
    pub fn add_to_waitlist(){}
    fn private_function(){}
  }
}

use front_of_house::hosting;

pub fn eat_at_restaurant(){
  hosting::add_to_waitlist();
//   hosting::private_function(); // 打开注释则会报错,和上述私有属性一样的错误
}

as

为引入的路径指定本地的别名(和js一样)

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

use crate::front_of_house::hosting as newhost;
fn main(){
    newhost::add_to_waitlist();
}

使用外部包

在Cargo.toml 中添加依赖的包,之后运行 cargo build 时候程序会 crates.io 寻找并下载依赖库(不能科学上网会有点卡,自行百度切换源即可) 示例:

# 修改 Cargo.toml
[package]
name = "variables"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rand = "0.8.5"
use rand::Rng;

fn main() {
    // 生成一个0~9的的随机数,gen_range 根据Rust 版本不同参数写法上也是有区别的需要注意
    // 较新版本的是 gen_range(0,10)
    let randnum = rand::thread_rng().gen_range(0..10);
    println!("Rand num is :  {}", randnum);
}

将模块分割进不同文件

(这块很有意思,对我这种基础不太好的产生了另一种思考方式) 当前我们都是将所有模块放到一个文件中,而当模块变得更大时,一个文件是无法承载如此多的内容的, 此时我们可以拆分文件,而rust有如下解析机制:

  • 模块定义时如果模块名后面是“;” ,而不是代码块,则rust会从与模块同名的文件或文件夹中加载模块
  • 无论如何加载,模块数的结构不会发生变化

示例:

// src/front_of_house.rs
pub mod hosting{
    pub fn add_to_waitlist(){}
    fn seat_at_table(){}
  }
// src/lib.rs
mod front_of_house;
use crate::front_of_house::hosting;

fn eat_at_restaurant(){
  hosting::add_to_waitlist();
}

当front_of_house文件又变得更大的时候我们可以继续拆分,按照结构,此时仿照lib.rs改变front_of_house.rs内代码:

// src/front_of_house.rs
pub mod hosting;

接着,创建一个名为front_of_house的文件夹,并在文件夹下创建一个 hosting.rs文件,结构变为 此时hosting.rs文件:

// src/front_of_house/hosting.rs
pub fn add_to_waitlist(){}
pub fn seat_at_table(){}

之后即可正常运行了。