《Rust权威指南》 第7章 使用包、单元包、模块来管理项目


Rust的模块系统包括

  • 包(package):一个用于构架、测试并分享单元包的功能
    • 单元包(crate):一个用于生成库或可执行文件的树形模块结构
      • 模块(module)和use关键字:控制文件结构,作用域及路径的私有性
  • 路径(path):一种用于命名条目(结构体、函数、模块)的方法

包与单元包

crate类型

  • 二进制(binary)
  • 库(library)

crate 作用

  • 将相关的功能组合到一个作用域内,便于在项目间进行共享

crate root

  • 是源代码文件
  • Rust编译器从这里开始,组成Crate的根Module
    • Cargo 把 crate root 文件交给 rustc 来构建 library 或 binary

一个package

  • 包含一个Cargo.toml,描述了如何构建这些Crates
  • 至少有一个crate
    • 最多包含一个library crate
    • 可以包含任意数量的library crate

使用 cargo new 创建出来package
在这里插入图片描述

一些惯例:

  • 在某个package下

    • src/main.rs
      • binary crate 的 crate root
      • crate名与 package名相同
    • src/lib.rs
      • 代表该package包含一个library crate
      • library crate 的 crate root
      • crate名与 package名相同
  • 一个 package 可以有多个 binary crate

    • 文件放在 src/bin里面
    • 每个文件是单独的binary crate

通过定义模块(module)来控制作用域及私有性

Module

  • 在一个crate内,将代码进行分组
  • 增加可读性。易于复用
  • 控制项目的私有性

建立Module

  • mod 关键字:mod 模块名 {...}
  • 可嵌套
  • module内可包含其他项(结构体、枚举、常量、trait、函数等)的定义

例子:在package my_project下的src/lib.rs文件中,写入下面代码

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.rssrc/lib.rs称为crate root就体现在这,这两个文件的内容各自组成了名为crate的模块,位于crate模块结构的根部
  • 这个模块结构也称为模块树。该例子展示的模块树是下面这样的在这里插入图片描述

将模块拆分为不同文件

可以将模块的定义放在和模块名同名的文件中。
注意,模块的文件树结构要和上面的模块树结构完全一致。比如将上面的模块完全拆分,文件结构是
在这里插入图片描述
src/lib.rs

mod front_of_house ;

src/front_of_house.rs

mod hosting ;
mod serving ;

src/front_of_house/hosting.rs

fn add_to_waitlist () {}
fn seat_at_table() {}

用于在模块树中指明条目的路径

与文件系统非常相似
路径有两种格式

  • 使用单元包的名称或字面量crate从根节点开始的绝对路径
  • 使用selfsuper,或当前模块名来从当前模块开始的相对路径
  • 多个标识符之间用::分割

使用例子:下面这个例子会报错,原因马上就会解释

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

基于实际情况进行决策,通常使用绝对还是相对路径取决于你是否会同时移动 条目的定义代码和使用该条目的代码

  • 是,基本上使用相对路径方便
  • 不是,使用绝对路径

使用super关键字构造相对路径

像在文件系统中使用../一样,表示当前位置的父级节点

fn serve_order() {}

mod back_of_house {
    fn fix_order() {
        cook_order();
        super::serve_order();
    }
    fn cook_order() {}
}

使用pub关键字来暴露路径

私有边界

上面的代码无法通过编译,是因为模块定义了Rust中的私有边界

  • Rust中的所有条目(函数、方法、结构体、枚举、模块、常量)默认都是私有的
  • 处于父级模块的条目无法使用子级模块的私有条目,子级模块的条目可以使用所有祖先模块中的条目

如何修改上面的代码?

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist () {}
    }
}
  • 第一个pub使得我们在访问模块front_of_house时能访问其子模块hosting
  • 第二个pub使得我们在访问模块hosting时能访问里面的函数add_to_waitlist

将结构体或枚举声明为pub

结构体

  • 在结构体定义前加pub,结构体本身成为公共的,但是其字段依旧保持私有状态
  • 我们可以在字段前加pub来逐一决定某字段是否公开
mod back_of_house{
    pub struct Breakfast {
        pub toast : String,
        seasonal_fruit : Srting,
    }
    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("Wheal"); //合法
    //接下来不合法
    //meal.seasonal_fruit = String::from("berries");
}

一点细节:由于结构体有私有的字段,所以提供一个共有的构造函数来进行初始化(在C++中我们太熟悉这一套了)

枚举

与结构体不同,将一个枚举声明为pub时,连带其所有字段也都是pub的了

mod back_of_house{
    pub enum Appetizer {
        Soup,
        Salad,
    }
}

pub fn eat_at_restaurant() {
    let order1 = back_of_house::Appetizer::Salad;
}

使用 use 关键字将路径导入作用域

使用use将路径导入作用域,并且像使用本地条目一般调用路径中的条目

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

use crate::front_of_house::hosting;

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

通过use crate::front_of_house::hosting;hosting成为该作用域下的有效名称
上面展示的是绝对路径,而使用相对路径时,对于从当前节点起始的符号串,必须以self开始

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

use self::hosting;

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

创建use路径时的惯用模式

  • 对于函数而言,通常引入到函数的父模块。之后调用函数时,通过模块名::函数名避免重名函数的风险和更好的阅读性
  • 对于结构体和枚举,直接引入完整路径。比如use std::collections::HashMap;

使用as关键字提供新名称

为了防止重名,可以用 use a as b来为引入的条目重命名(像是python的 import as 那样)

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

使用pub use 重导出名称

  • 使用普通的use ...导入时,该名称以私有的方式在新的作用域中生效
  • 使用pub use ...导入后,外部代码也能访问到该名称,甚至可以被外部代码从这个新的作用域引入

使用嵌套的路径来整理use语句

使用这样的语法路径相同部分::{不同部分,不同部分,...}

比如合并

use std::cmp::Ordering;
use std::io;

use std::{io,cmp::Ordering};

特殊情况是,使用self来代表“当前节点”
合并

use std::io;
use std::io::Write;

use std::io::{self,Write};

通配符*

将定义在某个路径中的所有公共条目都导入作用域,在指定路径时最后使用*

use std::collections::*;
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值