Rust包和crate以及模块

 

概念

  crate 是一个二进制项或者库。crate root 是一个源文件,Rust 编译器以它为起始点,并构成你的 crate 的根模块(我们将在 “DefiningModules to Control Scope and Privacy” 一节深入解读)。包(package) 是提供一系列功能的一个或者多个 crate。一个包会包含有一个 Cargo.toml 文件,阐述如何去构建这些 crate。包中所包含的内容由几条规则来确立。一个包中至多 只能 包含一个库 crate(library crate);包中可以包含任意多个二进制 crate(binary crate);包中至少包含一个 crate,无论是库的还是二进制的。

$ 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。

  因为一个 crate 的功能是在自身的作用域进行命名的,当我们将 rand 作为一个依赖,编译器不会混淆 Rng 这个名字的指向。在我们的 crate 中,它指向的是我们自己定义的struct Rng 。我们可以通过 rand::Rng 这一方式来访问 rand crate 中的 Rng 特性。
 
定义模块来控制作用域与私有性
  我们会从下面几个方面讲:允许你命名项的 路径(paths);用来将路径引入作用域的 use 关键字;以及使项变为公有的 pub 关键字。我们还将讨论 as 关键字、外部包和 glob 运算符。现在,让我们把注意力放在模块上!
 
模块
  模块 让我们可以将一个 crate 中的代码进行分组,以提高可读性与重用性。模块还可以控制项的 私有性,即项是可以被外部代码使用的(public),还是作为一个内部实现的内容,不能
被外部代码使用(private)。
  在餐饮业,餐馆中会有一些地方被称之为 前台(front of house),还有另外一些地方被称之为 后台(back of house)。前台是招待顾客的地方,在这里,店主可以为顾客安排座位,服务员接受顾客下单和付款,调酒师会制作饮品。后台则是由厨师工作的厨房,洗碗工的工作地点,以及经理做行政工作的地方组成。
  我们可以将函数放置到嵌套的模块中,来使我们的 crate 结构与实际的餐厅结构相同。通过执行 cargo new --lib restaurant ,来创建一个新的名为 restaurant 的库。然后将示例 7-1 中所罗列出来的代码放入 src/lib.rs 中,来定义一些模块和函数。
mod front {
    //  使用mod来定义模块,里面包含两个模块,其中里面有一些自定义的功能函数
    mod hosting {
        fn add_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 模块结构,称为 模块树(module tree)。这个树展示了一些模块是如何被嵌入到另一个模块的(例如, hosting 嵌套在front_of_house 中)。这个树还展示了一些模块是互为 兄弟(siblings) 的,那如果A模块包含B模块则,A模块称为父模块,B称为子模块

 
路径用于引用模块树中的项
  路径有两种形式:
    绝对路径(absolute path)从 crate 根开始,以 crate 名或者字面值 crate 开头。
    相对路径(relative path)从当前模块开始,以 self 、 super 或当前模块的标识符开头。
  绝对路径和相对路径都后跟一个或多个由双冒号( :: )分割的标识符。让我们来看看如何调用一个模块中的函数使用路径:
mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}
    }
}

pub fn eat_at_restaurant() {  // 一个 公共API,所以我们使用 pub 关键字来标记它,是他暴露
    // 绝对路径
    crate::front_of_house::hosting::add_to_waitlist(); // add_to_waitlist 函数与 eat_at_restaurant 被定义在同一 crate 中,这意味着我们可以 使用 crate 关键字为起始的绝对路径。

    // 相对路径
    front_of_house::hosting::add_to_waitlist();
}

  接下来让我们编译(cargo build) 下上面的代码,会出现如下报错:front_of_house::hosting::add_to_waitlist(); ^^^^^^^ private module; 也就是说路径是没问题的,但是 Rust 不让我们使用,因为它不能访问私有片段,Rust 中默认所有项(函数、方法、结构体、枚举、模块和常量)都是私有的。父模块中的项不能使用子模块中的私有项,但是子模块中的项可以使用他们父模块中的项。这是因为子模块封装并隐藏了他们的实现详情,但是子模块可以看到他们定义的上下文。继续拿餐馆作比喻,把私有性规则想象成餐馆的后台办公室:餐馆内的事务对餐厅顾客来说是不可知的,但办公室经理可以洞悉其经营的餐厅并在其中做任何事情。你还可以通过使用 pub 关键字来创建公共项,使子模块的内部部分暴露给上级模块。

 
使用 pub 关键字暴露路径
  也就是说由于Rust的私有性,要求模块内部的内容要被外部访问就必须使用pub来暴露。
mod front_of_house {
    pub mod hosting {
        // mod hosting前添加了pub关键字,使其变成公有的
        pub fn add_to_waitlist() {} // 如果我们可以访问 front_of_house ,那我们也可以访问 hosting 。但是 hosting 的 内容 (contents) 仍然是私有的;这表明使模块公有并不使其内容也是公有的。模块上的 pub 关键字只允许其父模块引用它。
    }
}

pub fn eat_at_restaurant() {
    // 一个 公共API,所以我们使用 pub 关键字来标记它,是他暴露
    // 绝对路径
    crate::front_of_house::hosting::add_to_waitlist(); // add_to_waitlist 函数与 eat_at_restaurant 被定义在同一 crate 中,这意味着我们可以 使用 crate 关键字为起始的绝对路径。

    // 相对路径
    front_of_house::hosting::add_to_waitlist();
}

  

 
posted @ 2020-06-22 09:39  独角兕大王  阅读(3695)  评论(0编辑  收藏  举报