1. Rust代码组织

代码组织可以决定哪些细节可以暴露,哪些细节是私有的,作用域内哪些名称有效。。。

模块系统:

  • package(包):cargo特性,让你构建、测试、共享crate
  • crate(单元包):一个模块数,它可以产生一个library或可执行文件
  • module(模块)、use:让你控制代码的组织、作用域、私有路径
  • path(路径):为struct、function或module等命名的方式

代码组织

1.1 package和crate

对于一个package,有以下规定:

  • 包含一个Cargo.toml,它描述了如何构建这些crates
  • 最多包含一个library crate
  • 可以包含任意数量的binary crate
  • 至少包含一个crate(library或binary)

crate有两种类型:binary和library,有一个crate root,是源代码文件,Rust编译器从这里开始,组成crate的根module

1.2 cargo的惯例

创建一个新的Rust项目,命名为my-project

cargo new my-project

出现文件src/main.rs,该文件是binary crate的crate root,crate名与package名相同

若出现src/lib.rs(一般不会),该文件表示package包含一个library crate,其为library crate的crate root,crate名与package名相同

cargo把crate root文件交给rustc来构建library或binary

一个package可以同时包含src/main.rssrc/lib.rs,名称与package名相同。一个package可以同时有多个binary crate,文件必须放在src/bin中,每个文件是单独的binary crate

1.3 定义module来控制作用域和私有性

使用module有以下好处:

  • 在crate内,将代码进行分组
  • 增加可读性,易于复hexo
  • 控制项目(item)的私有性(public、private)
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(){}
}
}

2. Path路径

为了在Rust的模块中找到某个条目,需要使用路劲

路径有两种形式:

  • 绝对路径:从crate root开始,使用crate名或字面值crate
  • 相对路径:从当前模块开始,使用self,super或当前模块的标识符

路径至少由一个标识符组成,标识符之间使用::

以下例子来自文件src/lib.rs,即拥有lib crate root

mod front_of_house
{
pub mod hosting
{
pub fn add_to_waiting(){}
}
}

pub fn eat_at restaurant()
{
crate::front_of_house::hosting::add_to_waitlist();//绝对路径
front_of_house::hosting::add_to_waitlist();//相对路径
}

2.1 Privacy Boundary私有边界

模块不仅可以组织代码,还可以定义私有边界。如果想把函数或者struct等设为私有,可以将它放在某个模块中。定义规则:

  • Rust中的所有条目(函数,方法,struct,enum,模块,常量)默认是私有的。
  • 父级模块无法访问子模块中的私有条目。
  • 子模块可以使用所有祖先条目。
  • 同级条目可以互相访问。
  • 使用pub关键字,将条目标记为公共。

2.2 super关键字

利用super关键字来访问父级模块路径中的内容,类似文件系统中的..

fn server_order(){}

mod back_of_house
{
fn fix_incorrect_order()
{
cook_order();
super::server_order();
}
fn cook_order(){}
}

2.3 pub structpub enum

pub放在struct前,struct变为公共的,但是struct的字段还是默认私有的;struct的字段需要单独设置pub来变成共有的。

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::Breakfast::summer("Rye");
meal.toast = String::from("Wheat");
meal.seasonal_fruit = String::from("blueberries");//报错
}

pub放在enum前,enum是公共的,里面的变体也是公共的

3. use关键字

可以使用use关键字将路径导入作用域中,仍然遵循私有性规则。

相对路径、绝对路径都可以

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

3.1 use的习惯用法

  • 函数:针对函数一般引入其父模块,防止函数重名情况(父级)

  • struct,enum:指定完整路径(本身)

    use std::collections::HashMap;//结构体

    fn main()
    {
    let mut map = HashMap::new();
    map.insert(1,2);
    }
  • 同名条目:父级,比如两个定义在不同模块的同名struct,要指定到父级

    use std::fmt;
    use std::io;

    fn f1() -> fmt::Result{}

    fn f2()-> io::Result{}

    fn main(){}

    也可以使用as关键字,为引入的路径指定本地的别名

    use std::fmt::Result;
    use std::io::Result as IoResult;

    fn f1() -> Result{}

    fn f2()-> IoResult{}

    fn main(){}

3.2 使用pub use重新导入名称

使用use将路径导入到作用域内后,该名称在此作用域内私有,pub use重导出,将条目引入作用域,该条目可以被外部代码引入到它们的作用域

3.3 使用外部包(package)

cargo.toml添加依赖的包(package)

标准库(std)也被当做外部包,但是不需要修改cargo.toml添加,需要使用use将std中特定的条目引入当前作用域

3.4 使用嵌套路径清理大量的use语句

可以使用嵌套路径在同一行内将同一个包或模块下的多个条目进行引入

路径相同的部分::{路径差异的部分}
use std::cmp::Ordering;
use std ::io;
//相当于
use::std::{cmp::Ordering,io};

//特殊情况
use std::io;
use std::io::Writing;
//相当于
use std::io::{self,Writing};
fn main(){}

3.5 通配符*

可以使用通配符*将某个模块下的所有条目引入,但是要谨慎使用。使用场景:

  • 测试,将所有被测试代码引入到tests模块
  • 有时被用于预导入(prelude)模块
use std::collections::*