rust学习-项目模块

伴随着项目的增长,通过将代码分解为多个模块和多个文件来组织代码
伴随着包的增长,将包中部分代码提取出来,做成独立 crate,即外部依赖项
一个包可以包含多个二进制 crate 项和一个可选的 crate 库。
包(Packages):
Cargo 的一个功能,它允许构建、测试和分享 crate
Crates :
一个模块的树形结构,它形成了库/二进制项目
包(package) 是提供一系列功能的一个或者多个 crate。一个包会包含有一个 Cargo.toml 文件,阐述如何去构建这些 crate

模块(Modules)

模块可以将一个 crate 中的代码进行分组,以提高可读性与重用性。
模块可以控制项的私有性,即项是可以/不可以被外部代码使用的(public)
模块可以包含其他项,比如结构体、枚举、常量、trait、函数
执行 cargo new --lib restaurant,来创建一个名为 restaurant 的库

Filename: src/lib.rs
// mod 定义一个模块
mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn server_order() {}

        fn take_payment() {}
    }
}

src/main.rs 和 src/lib.rs 被称为 crate 根,是因为这两个文件中任意一个的内容会构成名为 crate 的模块,且该模块位于 crate模块树的根部

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

可以理解为一个文件系统

模块不仅用于组织代码。还用于私有性边界(privacy boundary):
这条界线不允许外部代码了解、调用和依赖被封装的实现细节。
如果希望创建一个私有函数或结构体,可以将其放入模块。

默认所有项(函数、方法、结构体、枚举、模块和常量)都私有
父模块中的项不能使用子模块中的私有项
子模块中的项可以使用父模块中的项
通过使用 pub 关键字来创建公共项,使子模块的内部部分暴露给上级模块

拿餐馆作比喻,把私有性规则想象成餐馆的后台办公室:
餐馆内的事务对餐厅顾客来说是不可知
办公室经理洞悉其经营的餐厅并在其中做任何事情

路径(path)

路径用于引用模块树中的项
(1)绝对路径(absolute path):从 crate 根部开始,以 crate 名或者字面量 crate 开头。
(2)相对路径(relative path):从当前模块开始,以 self、super 或当前模块的标识符开头

示例

mod front_of_house {
	// hosting 公开,如果add_to_waitlist不公开,则仍无法访问
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

// eat_at_restaurant 和 front_of_house 位于同一模块,所以可以调用
// eat_at_restaurant 和 front_of_house 是兄弟
pub fn eat_at_restaurant() {
    // Absolute path
    crate::front_of_house::hosting::add_to_waitlist();

    // Relative path
    front_of_house::hosting::add_to_waitlist();
}

super

fn serve_order() {}

mod back_of_house {
    fn fix_incorrect_order() {
        cook_order();
        super::serve_order(); // 类似于文件系统的..
        // 使用 super 进入 back_of_house 父模块,也就是本例中的 crate 根
        // 如果这些代码被移动到了其他模块,只需要更新很少的代码
    }

    fn cook_order() {}
}

结构体

如果在一个结构体定义的前面使用 pub ,这个结构体变成公有
但这个结构体的字段仍是私有。可以根据情况决定每个字段是否公有

mod back_of_house {
    pub struct Breakfast {
        pub toast: String,
        seasonal_fruit: String,
    }

    impl Breakfast {
        //  back_of_house::Breakfast 具有私有字段
        // 所以这个结构体需要提供一个公共的关联函数来构造 Breakfast 的实例
        // 如果 Breakfast 没有这样的函数,则无法在eat_at_restaurant中创建meal
        pub fn summer(toast: &str) -> Breakfast {
            Breakfast {
                toast: String::from(toast),
                seasonal_fruit: String::from("peaches"),
            }
        }
    }
}

pub fn eat_at_restaurant() {
    // Order a breakfast in the summer with Rye toast
    let mut meal = back_of_house::Breakfast::summer("Rye");
    // Change our mind about what bread we'd like
    meal.toast = String::from("Wheat");
    println!("I'd like {} toast please", meal.toast);

    // The next line won't compile if we uncomment it; we're not allowed
    // to see or modify the seasonal fruit that comes with the meal
    // meal.seasonal_fruit = String::from("blueberries");
}

枚举

枚举成员默认就是公有

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

use 将名称引入作用域

背景

用于调用函数的路径都很冗长且重复,使用 use 关键字将路径一次性引入作用域,然后调用该路径中的项,就如同它们是本地项一样

示例

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

// 在作用域中增加 use,有点类似于在文件系统增加软链接 symbolic link
// hosting 在作用域中就是有效的名称,如同 hosting 模块被定义于 crate 根一样
// 绝对路径的示例
use crate::front_of_house::hosting;

// 相对路径的示例
// use front_of_house::hosting;

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

何为习惯

反例

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

// use将函数的父模块引入作用域,必须在调用函数时指定父模块
// 这样可以清晰地表明函数不是在本地定义,同时使完整路径的重复度最小化
// 不然调用函数的地方不知道add_to_waitlist来自哪里
use crate::front_of_house::hosting::add_to_waitlist;

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

正例

// 结构体和上述相反,使用的是全链路
use std::collections::HashMap;

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

使用父模块将两个具有相同名称的类型引入同一作用域

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

// 两个Result来源不一样
fn function1() -> fmt::Result {
    // --snip--
}

fn function2() -> io::Result<()> {
    // --snip--
}

要么as提供别名


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

fn function1() -> Result {
    // --snip--
}

fn function2() -> IoResult<()> {
    // --snip--
}

pub use 重导

pub use 重导出名称
使用 use 关键字将名称导入作用域时,在新作用域中可用的名称私有
通过 pub use 使名称可引入任何代码的作用域中

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

如果没有指定 pub use,eat_at_restaurant 函数可以在其作用域中调用 hosting::add_to_waitlist,但外部代码则不允许使用这个新路径

外部包

示例:项目使用了一个外部包rand,来生成随机数,提前修改 Cargo.toml
use 起始的包名,它以 rand 包名开头并列出了需要引入作用域的项

use rand::Rng;

fn main() {
    let secret_number = rand::thread_rng().gen_range(1..101);
}

标准库(std)对于其他包来说也是外部 crate
因为标准库随 Rust 语言一同分发,无需修改 Cargo.toml 来引入 std
但需要通过 use 将标准库中定义的项引入项目包的作用域中来引用
比如使用 HashMap:

// 以标准库 crate 名 std 开头的绝对路径
use std::collections::HashMap;

嵌套路径消除 use 行

// 兄弟
use std::{cmp::Ordering, io};

// 父子
use std::io::{self, Write};

glob 运算符将所有的公有定义引入作用域

glob 运算符经常用于测试模块 tests 中

use std::collections::*;
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值