rust 学习记录 VII 包和crete和模块管理

包和 crate 和模块管理

概念

主要包含以下的几个概念:

  • 包(Packages): Cargo 的一个功能,它允许你构建、测试和分享 crate。
  • Crates :一个模块的树形结构,它形成了库或二进制项目。
  • 模块(Modules)和 use: 允许你控制作用域和路径的私有性。
  • 路径(path):一个命名例如结构体、函数或模块等项的方式

包和 crate

我们这里直接看官方的解释,看着比较复杂,但实际上 rust 和 cargo 都帮我们做好了。

我们输入命令 cargo new:

$ cargo new my-project
     Created binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rs

当我们输入了这条命令,Cargo 会给我们的包创建一个 Cargo.toml 文件。查看 Cargo.toml 的内容,会发现并没有提到 src/main.rs,因为 Cargo 遵循的一个约定:src/main.rs 就是一个与包同名的二进制 crate 的 crate 根。同样的,Cargo 知道如果包目录中包含 src/lib.rs,则包带有与其同名的库 crate,且 src/lib.rs 是 crate 根。crate 根文件将由 Cargo 传递给 rustc 来实际构建库或者二进制项目。

在此,我们有了一个只包含 src/main.rs 的包,意味着它只含有一个名为 my-project 的二进制 crate。如果一个包同时含有 src/main.rs 和 src/lib.rs,则它有两个 crate:一个库和一个二进制项,且名字都与包相同。通过将文件放在 src/bin 目录下,一个包可以拥有多个二进制 crate:每个 src/bin 下的文件都会被编译成一个独立的二进制 crate。

模块和路径

这里也直接放代码了,我们用 mod 来声明一个模块,用 pub 来表明这个模块是公开的,比如我们在 src/lib.rs 中写了如下的代码:

#![allow(unused)]

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

        pub fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {
            // 此处使用了 super 作为相对路径,super 代表了父模块
            super::hosting::add_to_waitlist();
        }
    }
}

fn main() {
    pub fn eat_at_restaurant() {
        // Absolute path
        crate::front_of_house::hosting::add_to_waitlist();

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

这时候 rust 就会生成如下的模块树

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

我们可以根据这里面的模块是否公开来使用他们,使用的时候需要注意路径。

路径分为绝对路径和相对路径

  • 绝对路径:即直接根据 crate 来写
    • crate::front_of_house::hosting::add_to_waitlist()
  • 相对路径:相对于同一层级
    • front_of_house::hosting::add_to_waitlist()

此外我们还可以使用 super 代表父模块从而来表达路径:

// 此处使用了 super 作为相对路径,super 代表了父模块
super::hosting::add_to_waitlist();

使用 pub 暴露路径

如果一个模块没有使用 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");
    println!("I'd like {} toast please", meal.toast);

    // 如果取消下一行的注释,将会导致编译失败;我们不被允许
    // 看到或更改随餐搭配的季节水果
    // meal.seasonal_fruit = String::from("blueberries");
}

这里我们通过结构体的函数返回了一个实例,但是由于 season_fruit 没有 pub,所以我们无法改变它的值。

但是枚举类型并不需要这样,枚举类型只需要将枚举前面加上 pub 关键字即可,这样我们直接访问枚举值。

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 的方式导入一个模块,以后我们就可以直接使用这个模块中的数据了。相当于是在 crate 根中增加了一个软链接。

#![allow(unused)]

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

        pub fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {
            // 此处使用了 super 作为相对路径,super 代表了父模块
            super::hosting::add_to_waitlist();
        }
    }
}

// -------------------------------------------------> use 关键字
use front_of_house::hosting;

fn main() {
    pub fn eat_at_restaurant() {
        // Absolute path
        crate::front_of_house::hosting::add_to_waitlist();

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

那我们为什么不直接引入到函数呢,这样直接使用函数不也可以吗?

实际上肯定是可以的,但是我们为了在开发中能够明确自己用的函数、结构体等等是哪一个模块中的,这样写更符合模块化的规范。

此外我们在引入的同时,还可以使用 as 关键字指定别名:

// -------------------------------------------------> use 关键字
use front_of_house::hosting as host;

fn main() {
    pub fn eat_at_restaurant() {
        // Absolute path
        crate::front_of_house::hosting::add_to_waitlist();

        // Relative path
        host::add_to_waitlist();
    }
}

re-exporting

我们使用 use 之后,在我们编写的代码的作用域中,当然就可以直接使用了,但是当在其他的代码中引入我们编写的代码,这时候还是无法使用的,这时候就需要使用 pub 关键字:

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

这样,在任何代码的作用域中,只要引入了这个模块,就同样可以使用这个模块中引入的其他模块。

嵌套路径

当引入的路径过多是,头部会显得比较臃肿,rust 允许我们在引入的时候进行嵌套:

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

// 可以被合并为
use std::{cmp::Ordering, io};


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

// 可以使用 self 表示自身
use std::io::{self, Write};

引入所有公共定义

use std::collections::*;

模块化

rust 模块化引入的文件位置和文件本身的位置有关,需要按照约定构建目录。

image.png

A 类:

image.png

B 类:

image.png

  • 9
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

城南顾北

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值