使用包、crate和模块管理不断增长的项目(7)

使用包、crate和模块管理不断增长的项目

    • 1.包和crate
    • 2.定义模块来控制作用域与私有性
      • 2.1模块概括
        • 1.从crate根节点开始
        • 2.声明模块
        • 3.声明子模块
        • 4.模块中的代码路径
        • 5.私有 vs 公用
        • 6.use关键字
      • 2.2在模块中对相关代码进行分组
        • 1.一个包含了其他内置函数的模块front_of_house模块
    • 3.引用模块项目的路径
      • 3.1引用模块项目的路径
        • 1.路径
        • 2.模块访问规则
      • 3.2使用pub关键字暴露路径
      • 3.2super开始的相对路径
      • 3.3创建公有的结构体和枚举
        • 1.结构体
        • 2.枚举
    • 4.使用use关键字将路径引入作用域
      • 4.1使用use关键字
        • 1.使用use将模块引入作用域
        • 2.use语句只适用于其所在的作用域
      • 4.2创建惯用的use路径
        • 1.使用use将add_to_waitlist函数引入作用域,这并不符合习惯
        • 2.将HashMap引入作用域的习惯用法
        • 3.使用父模块将两个具有相同名称的类型引入同一作用域
      • 4.3使用as关键字提供新的名称
      • 4.4使用pub use重导出名称
      • 4.5使用外部包
      • 4.6嵌套路径来消除大量的use行
        • 1.指定嵌套的路径在一行中将多行带有相同前缀的项引入作用域
        • 2.通过两行use语句引入两个路径,其中一个时另一个的子路径
        • 3.部分重复的路径合并为一个use语句
        • 4.通过glob运算符将所有的公有定义引入作用域
    • 5.将模块拆分成多个文件
      • 1.模块拆分
        • 1.1声明front_of_house模块,其内容将位于src/front_of_house.rs
        • 1.2在src/front_of_house.rs中定义front_of_house模块
        • 1.3子模块的执行代码
      • 2.注意事项

1.包和crate

  • crate是Rust在编译时最小的代码单位
  • crate有两种形式: 二进制项
  • 库并没有main函数,它们也不会编译为可执行程序,它们提供一些诸如函数之类的东西
  • crate root是一个源文件,Rust编译器以它为起始点,并构成你的crate的根模块
  • 包中可以包含至多一个库;包中可以包含任意多个二进制crate,但是必须至少包含一个crate(无论是库还是二进制的)
  • 包是提供一些列功能的一个多个crate,一个包回包含一个Cargo.toml文件

2.定义模块来控制作用域与私有性

2.1模块概括

1.从crate根节点开始
  • 当编译一个crate,编译器首先在crate根文件(对于一个库crate而言是src/lib.rs,对于一个二进制crate而言是src/main.rs)中寻找需要被编译的代码
2.声明模块
  • crate根文件中,你可以声明一个模块;比如,使用mod garden声明了一个叫做garden的模块;编译器会在下列路径中寻找模块代码
    • 内联,在大括号中,当mod garden后方不是一个分号而是一个大括号
    • 在文件 src/garden.rs
    • 在文件 src/garden/mod.rs
3.声明子模块
  • 在除了crate根节点以外的其他文件中,你可以定义子模块;比如,使用src/garden.rs中定义了mod vegetables;编译器会在以父模块命名的目录中寻找子模块代码
    • 内联,在大括号中,当mod vegetables后方不是一个分号而是一个大括号
    • 在文件 src/garden/vegetables.rs
    • 在文件 src/garden/vegetables/mod.rs
4.模块中的代码路径
  • 一旦一个模块是你crate的一部分,可以在隐私规则允许的前提下,从同一个crate内的任意地方,通过代码路径引用该模块的代码
  • 一个garden vegetables模块下的Aspargus类型可以在crate::garden::vegetables::Asparagus被找到
5.私有 vs 公用
  • 一个模块里的代码默认对其父模块私有
  • 为了使一个模块公用,应当在声明时使用pub mod 替代 mod
  • 为了使一个公用模块内部的成员公用,应当在声明前使用pub
6.use关键字
  • 在一个作用域内,use关键字创建了一个成员的快建方式,用来减少长路径的重复
  • 在任何可以引用crate::garden::vegetables::Asparagus的作用域,通过use crate::garden::vegetables::Asparagus
  • 创建一个快捷方式,然后你就可以在作用域中只写Asparagus来使用该类型
backyard
|-Cargo.lock
|-Cargo.toml
|-src
	|-garden
	|	|-vegetables.rs
	|-garden.rs
	|-main.rs

src/garden/vegetables.rs

#[derive(Debug)]
pub struct Asparagus {}

src/garden.rs
pub mod vegetables;

src/main.rs
use crate::garden::vegetables::Asparagus;

pub mod garden;

fn main() {
	let  plant = Asparagus {};
	println!("I'm growing {:?}!",plant);
}

2.2在模块中对相关代码进行分组

  • 模块让我们可以将一个crate中的代码进行分组,以提高可读性重用性
  • 一个模块中的代码默认是私有的,还可以利用模块控制项的私有性
  • 可以将函数放置到嵌套的模块中,来使crate结构与实际餐厅结构相同
  • 通过执行cargo new --lib restaurant,来创建一个新的名为restaurant的库
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() {}
    }
}
1.一个包含了其他内置函数的模块front_of_house模块
  • src/main.rs和src/lib.rs叫做crate根
crate
	|-front_of_house
		|-hosting
		|	|-add_to_waitlist
		|	|-seat_at_table
		|-serving
			|-take_order
			|-serve_order
			|-take_payment
	

3.引用模块项目的路径

3.1引用模块项目的路径

1.路径
  • 绝对路径
    • 以crate根(root)开头的全路径;对于外部crate的代码,是以crate名开头的绝对路径,对于当前crate的代码,则以字面值crate开头
  • 相对路径
    • 从当前模块开始,以selfsuper当前模块的标识符开头
  • 绝对路径和相对路径都后跟一个或多个由双冒号(::)分割的标识符
########## error code  ####################
mod front_of_house {
   	pub mod hosting {
        fn add_to_waitlist() {}
        fn seat_at_table() {}
    }
    mod serving {
        fn take_order() {}
        fn serve_order() {}
        fn take_payment() {}
    }
}
pub fn eat_at_restaurant() {
    //绝对路径
    crate::front_of_house::hosting::add_to_waitlist();
    //相对路径
    front_of_house::hosting::add_to_waitlist();
}
2.模块访问规则
  • 在Rust中,默认所有项(函数、方法、结构体、枚举、模块和常量)对父模块都是私有的;希望创建一个私有函数或结构体,可以将其放在一个模块
  • 父模块中的项不能使用子模块中的私有项,但是子模块中的项可以使用它们父模块中的项

3.2使用pub关键字暴露路径

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
        fn seat_at_table() {}
    }
    mod serving {
        fn take_order() {}
        fn serve_order() {}
        fn take_payment() {}
    }
}
pub fn eat_at_restaurant() {
    //绝对路径
    crate::front_of_house::hosting::add_to_waitlist();
    //相对路径
    front_of_house::hosting::add_to_waitlist();
    // 模块中的内容(函数等)都是私有化的
    //crate::front_of_house::hosting::seat_at_table();
}

3.2super开始的相对路径

  • 通过在路径的开头使用super,从父模块开始构建相对路径,而不是从当前模块crate根开始
fn deliver_order() {}
mod back_of_house {
    fn fix_incorrect_order() {
        cook_order();
        super::deliver_order();
    }
    fn cook_order() {}
}

3.3创建公有的结构体和枚举

1.结构体
mod back_of_house {
    pub struct Breakfast {
        pub toast: String,
        seasonal_fruit: String,
    }
    impl Breakfast {
        pub fn summer(toast: &str) -> Breakfast {
            Breakfast {
                toast: String::from(toast),
                seasonal_fruit: String::from("peaches"),
            }
        }
    }
}
pub fn eat_at_restaurant() {
    //在夏天订购一个黑麦土司作为早餐
    let mut meal = back_of_house::back_of_house::summer("Rye");
    //改变注意更换面包
    meal.toast = String::from("Wheat");
    println!("I'd like {} toast please", meal.toast);

    //不允许查看或修改早餐附加的季节水果
    //meal.seasonal_fruit = String::from("Banana");
}

2.枚举
mod back_of_house{
	pub enum Appetizer{
		Soup,
		Salad,
	}
}
pub fn eat_at_restaurant(){
	let order1 = back_of_house::Appetizer::Soup;
	let order2 = back_of_house::Appetizer::Salad;
}

4.使用use关键字将路径引入作用域

4.1使用use关键字

1.使用use将模块引入作用域
  • create::front_of_house::hosting模块引入了eat_at_restaurant函数的作用域
  • 需要指定hosting::add_to_waitlist即可在eat_at_restaurant中调用add_to_waitlist函数
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}
2.use语句只适用于其所在的作用域
  • 作用域中use路径类似于在文件系统中创建软连接(符号连接,symbolic link)
  • 通过在crate根增加use crate::front_of_house::hosting,hosting在作用域中就是有效的名称
  • 通过use引入作用域的路径也会检查私有性,同其他路径一样

############### 编译器错误显示短路不在适用于customer #####################

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}
use crate::front_of_house::hosting;

mod customer {
    pub fn eat_at_restaurant() {
        hosting::add_to_waitlist();
    }
}

  • 为了修复这个问题,可以将use移动到customer模块内,或者在子模块customer内通过super::hosting引用父模块中的这个短路径
use crate::front_of_house::hosting;
mod customer {
	// 方式一
	// use crate::front_of_house::hosting;
	
	super::hosting;
	pub fn eat_at_restaurant() {
		hosting::add_to_waitlist();
	}
}

4.2创建惯用的use路径

1.使用use将add_to_waitlist函数引入作用域,这并不符合习惯
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}
use crate::front_of_house::hosting::add_to_waitlist;

mod customer {
    pub fn eat_at_restaurant() {
        add_to_waitlist();
    }
}
2.将HashMap引入作用域的习惯用法
  • 使用use引入结构体枚举其他项时,习惯是指定它们的完整路径
use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert(1, 2);
}

3.使用父模块将两个具有相同名称的类型引入同一作用域
use std::fmt;
use std::io;

fn function1() -> fmt::Result{}
fn function2() -> io::Result{}

4.3使用as关键字提供新的名称

  • 使用use将两个同名类型引入同一作用域这个问题还是另一个解决办法
  • 在这个类型的路径后面,使用as指定一个新的本地名称或者别名
use std::fmt::Result;
use std::io::Result as IoResult;
fn function1() -> Result {}
fn function2() -> IoResult {}

4.4使用pub use重导出名称

  • 使用use关键字,将某个名称导入当前作用域,这个名称在此作用域就可以使用,对此作用域之外还是私有的
  • pubuse 合起来使用,就能适用于其他作用域
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}
pub use crate::front_of_house::hosting;

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

  • 未使用pub use之后,外部代码需要使用路径 restaurant::front_of_house::hosting::add_to_waitlist()来调用add_to_waitlist函数;外部代码现在可以使用路径restaurant::hosting::add_to_waitlist

4.5使用外部包

  • 在Cargo.toml中加入rand依赖告诉了Cargo要从crate.io下载rand和其依赖,并使其可在项目代码中使用
  • Cargo.toml
[package]
name = "randNum"
version = "0.1.0"
edition = "2021"

[dependencies]
rand = "0.8.5"
  • main.rs
use rand::Rng;
fn main() {
    let secret_number = rand::thread_rng().gen_range(1..=100);
    println!("随机数:{}", secret_number);
}

  • 注意 std 标准库对于你的包来说也是外部crate
  • 标准库随Rust语言一同分发,无需修改Cargo.toml来引入 std
use std::collections::HashMap;

4.6嵌套路径来消除大量的use行

1.指定嵌套的路径在一行中将多行带有相同前缀的项引入作用域
  • 当需要引入很多定义于相同包相同模块的项时,为每一项单独列出一行占用源码很大的空间
use std::cmp::Ordering;
use std::io;

//简写
use std::{::cmp::Ordering,io};
2.通过两行use语句引入两个路径,其中一个时另一个的子路径
use std::io;
use std::io::write;
3.部分重复的路径合并为一个use语句
use std::io::{self,Write};
4.通过glob运算符将所有的公有定义引入作用域
  • 所有公有项引入作用域,可以指定路径后跟*,glob运算符
use std::collections::*;

5.将模块拆分成多个文件

1.模块拆分

mod front_of_house{
	pub mod hosting{
		pub fn add_to_waitlist(){}
	}
}
pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}
1.1声明front_of_house模块,其内容将位于src/front_of_house.rs
  • src/lib.rs
mod front_of_house;
pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}
1.2在src/front_of_house.rs中定义front_of_house模块
  • src/front_of_house.rs
pub mod hosting;

1.3子模块的执行代码
  • src/front_of_house/hosting.rs
pub fn add_to_waitlist(){}

2.注意事项

  • 一种老版本模块(目前也受支持)
    • src/front_of_house/mod.rs
    • src/front_of_house/hosting/mod.rs
  • 和以上新方式对比,了解不同

你可能感兴趣的:(Rust,rust,后端)