7

三言两语理解Rust module System

 2 years ago
source link: https://zhuanlan.zhihu.com/p/365735504
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

三言两语理解Rust module System

微信公众号:CrackingOysters

Rust 的module system容易让人摸不着头脑。The book写了很多的内容,但是不适合有一定编码经验的人。因为里面的内容更多的是强调module的好处,解决了什么问题,并没有直接给出如何使用module。本文关注怎么使用以及记录我的一些理解,方便与我有同样困惑的人,也方便我日后参考。

本文部分图片来源于Clear explanation of Rust’s module system

下面的第二点让我明白了rust 的”be explicit“原则无处不在。

Module System

一个问题几乎总会由许多小问题组成。module system是为了定义清楚各个小问题的边界。这样更容易和更方便的管理问题。而大问题的解法,就是把小问题的解法组合起来。

下面是Rust Module System让人困惑的三个地方。

1. project, package, crate, module的区别

project,package, crate, module这些概念感觉相似。实际上,一个package/project可以包含多个 binary crates和一个或者零个library binary。一个crate可以包含多个module。可以认为package就是一个project,一个crate就是一个暴露给外界的逻辑单元,一个module就是一个小问题的解法

比如下面的project 结构

v2-f49c664dc3a4c84a111c34bfaf5ceb46_720w.jpg

包含了一个library crate,还有两个binary crates:一个是main.rs(这个名字是随便起的,可以是其他的名字),一个是main2.rs。

project是通过cargo new --bin project_name来生成的。(cargo new --lib project_name生成带library crate的project)

当project里面有lib.rs说明这个project是一个library crate,这个library的名字是project的名字。main.rs/main2.rs都可以直接使用这个library crate。我们可以认为bin文件夹里面是单独的crate,它们默认导入了这个library crate。

2. module tree

project里面的module tree必须显示地指明,而且一开始的时候,编译器看到的project的module tree仅仅含有main.rs或者lib.rs(下图右边的黄色框框),又叫做crate root,就是crate的根节点。代码crate::foo 的crate指代的就是这个crate root。

上图左边的目录树,大部分人看到文件系统已经有结构并且组织好,会下意识地认为编译器也会看到这个文件目录结构。但是实际上,编译器看到的project仅仅包含了main.rs(上图右边,module tree仅仅有一个crate root,也就是main.rs)。这一点Rust跟其他语言区别很大。比如在Java里面,你可以直接import 某个目录里面的class。在Rust你直接use某个目录的module是编译通不过的。报错如下

error[E0432]: unresolved import `http_server::log`
 --> src/bin/main.rs:3:5
  |
3 | use http_server::log;
  |     ^^^^^^^^^^^^^^^^ no `log` in the root

因为你还没有将log添加到crate root里面。也就是要显示地指明module tree的结构。这也就是我们经常在main.rs/lib.rs里面看到许多mod xxx的原因。比如这里的代码https://github.com/Celthi/rsnova/blob/master/src/lib.rs#L20

它们的存在就是为了将project里面的modules 加到这个crate里面。比如在main.rs 里面看到mod channel,就是将module channel加进crate的module tree来。

3. foo.rs, ./foo/mod.rs

有时候一个folder里面有个mod.rs,有时候没有。这是因为mod foo;的作用是告诉编译器找寻foo.rs或者foo/mod.rs,并且将找寻到的文件内容作为module foo的内容。你可以选择你喜欢的方式使用foo.rs或者foo/mod.rs。使用文件夹foo存储modules的时候,我们可以创建一个跟文件夹同名的foo.rs来添加文件夹里面的modules,也可以在文件夹foo里面用mod.rs添加对应的modules.

所以,

mod foo;是声明模块,而./foo.rs或者./foo/mod.rs定义模块,而mod foo {... } 既声明又定义模块。

use foo;是将一个模块加进当前的scope

如果要类比,mod foo {... } 相当于C++里面定义一个变量比如int foo = 3。如果在另外的文件要使用这个变量,那么你要extern int foo(use foo)将这个变量带到当前的文件,这样你才能访问。

module的可见性,并不让人困惑,所以本文不提。

Rust module system最重要的提炼为下面三点

  • foo.rs或者foo/mod.rs, foo {... } 定义模块foo。
  • 子模块必须在父模块在声明(mod child),不然它们就不会存在。
  • 子模块的内容要么显示地内联定义在父模块里面,如 mod {... },要么定义在文件为./child.rs或者./child/mod.rs里面。

参看The confusion around Rust's modules reminds me of the different ways that people...

The Edition Guide

Mentally Modelling Modules
Rust modules confusion when there is main.rs and lib.rs


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK