pull/107/head
sunface 3 years ago
parent 6edaa7364a
commit d2185f3432

@ -48,8 +48,8 @@
- [返回和错误处理](basic/result-error/intro.md)
- [panic深入剖析!](basic/result-error/panic.md)
- [返回值Result和?](basic/result-error/result.md)
- [文档注释 todo](basic/comment.md)
- [Rust高级进阶 doing](advance/intro.md)
- [生命周期](advance/lifetime/intro.md)
- [认识生命周期](advance/lifetime/basic.md)
@ -57,11 +57,18 @@
- [函数式编程](advance/functional-programing/intro.md)
- [闭包closure](advance/functional-programing/closure.md)
- [迭代器iterator](advance/functional-programing/iterator.md)
- [包和模块](advance/crate-module/intro.md)
- [包crate](advance/crate-module/crate.md)
- [模块Module](advance/crate-module/module.md)
- [使用use引入](advance/crate-module/use.md)
- [工作空间workspace](advance/crate-module/workspace.md)
- [深入类型之newtype和Sized](advance/custom-type.md)
- [格式化输出](advance/formatted-output.md)
- [文档注释 todo](advance/comment.md)
- [包和模块 todo](advance/crate-module.md)
- [智能指针 todo](advance/smart-pointer.md)
- [智能指针 todo](advance/smart-pointer/intro.md)
- [Box对象(todo)](advance/smart-pointer/box.md)
- [Deref和Drop特征(todo)](advance/smart-pointer/deref-drop.md)
- [Rc与RefCell(todo)](advance/smart-pointer/rc-refcell.md)
- [自引用与内存泄漏(todo)](advance/smart-pointer/self-referrence.md)
- [全局变量 todo](advance/global-variable.md)
@ -82,17 +89,6 @@
- [性能测试](test/benchmark.md)
- [持续集成](test/ci.md)
- [日志和监控 todo](monitor/intro.md)
- [日志](monitor/log.md)
- [可观测性](monitor/observability.md)
- [监控(APM)](monitor/apm.md)
- [智能指针 todo](smart-pointer/intro.md)
- [Box对象(todo)](smart-pointer/box.md)
- [Deref和Drop特征(todo)](smart-pointer/deref-drop.md)
- [Rc与RefCell(todo)](smart-pointer/rc-refcell.md)
- [自引用与内存泄漏(todo)](smart-pointer/self-referrence.md)
- [常见特征解析 todo](traits/intro.md)
- [类型转换From/Into](traits/from-into.md)
- [AsRef, AsMut](traits/as-ref-as-mut.md)
@ -148,6 +144,7 @@
- [原生指针(todo)](unsafe/raw-pointer.md)
- [修改全局变量](unsafe/modify-global-var.md)
- [FFI外部语言用](unsafe/ffi.md)
- [那些会导致UB的代码](unsafe/ub.md)
- [对抗编译检查 doing](fight-with-compiler/intro.md)
- [幽灵数据(todo)](fight-with-compiler/phantom-data.md)
@ -175,6 +172,11 @@
- [常见属性标记](compiler/attributes.md)
- [优化编译速度](compiler/speed-up.md)
- [日志和监控 todo](monitor/intro.md)
- [日志](monitor/log.md)
- [可观测性](monitor/observability.md)
- [监控(APM)](monitor/apm.md)
- [标准库解析 todo](std/intro.md)
- [如何寻找你想要的内容](std/search.md)
- [Vector常用方法](std/vector.md)

@ -0,0 +1,99 @@
# 包和Package
当读者按照章节顺序读到本章时,意味着你已经几乎具备了参与真实项目开发的能力。但是真实项目远比我们之前的`cargo new`的默认目录结构要复杂好在Rust为我们提供了强大的包管理工具
- **Package**: 可以用来构建、测试和分享包
- **工作空间workspace**: 对于大型项目,可以进一步将多个包联合在一起,组织成工作空间
- **包Crate**: 一个由多个模块组成的树形结构,可以作为三方库进行分发,也可以生成可执行文件进行运行
- **模块Module**:可以一个文件多个模块,也可以一个文件一个模块,模块可以被认为是真实项目中的代码组织单元
## 定义
其实`Package`和包很容易被搞混,甚至在很多书中,这两者都是不分的,但是由于官方对此做了明确的区分,因此我们会在本章节中试图(挣扎着)理清这个概念.
#### 包crate
对于Rust而言包是一个独立的可编译单元它编译后会生成一个可执行文件或者一个库。
一个包会将相关联的功能打包在一起,使得该功能可以很方便的在多个项目中分享。例如标准库中没有提供但是在三方库中提供的`rand`包,它提供了随机数生成的功能,我们只需要将该包通过`use rand;`引入到当前项目的作用域中,就可以在项目中使用`rand`的功能: `rand::XXX`
同一个包中不能有同名的类型,但是在不同包中就可以。例如,虽然`rand`包中,有一个`Rng`特征,可是我们依然可以在自己的项目中定义一个`Rng`,前者通过`rand::Rng`访问,后者通过`Rng`访问,对于编译器而言,这两者的边界非常清晰,不会存在引用歧义。
## Package
鉴于Rust团队标新立异的起名传统以及包的名称被`crate`占用,库的名称被`library`占用,因此导致我不知道该如何准确的翻译`Package`,遂决定,不翻译,但是大家可以把它理解为一个项目工程,或者一个微服务工程。
由于`Package`就是一个项目,因此它包含有独立的`Cargo.toml`文件, 以及因为功能性被组织在一起的一个或多个包。一个`package`只能包含**一个**库(library)类型的包, 但是可以包含**多个**二进制可执行类型的包。
#### 二进制package
让我们来创建一个二进制`package`:
```console
$ cargo new my-project
Created binary (application) `my-project` package
$ ls my-project
Cargo.toml
src
$ ls my-project/src
main.rs
```
这里Cargo为我们创建了一个名称是`my-project`的`package`, 同时在其中创建了`Cargo.toml`文件,可以看一下该文件,里面并没有提到`src/main.rs`作为程序的入口, 原因是Cargo有一个惯例**src/main.rs是二进制包的根文件该二进制包的包名跟所属package相同在这里都是my-project**, 所有的代码执行都从该文件中的`fn main()`函数开始。
使用`cargo run`可以运行该项目,输出:`Hello, world!`.
#### 库package
再来创建一个库类型的`package`:
```console
$ cargo new my-test --lib
Created library `my-lib` package
$ ls my-lib
Cargo.toml
src
$ ls my-lib/src
lib.rs
```
首先,如果你试图运行`my-test`,会报错:
```console
$ cargo run
error: a bin target must be available for `cargo run`
```
原因是库类型的`package`只能作为三方库被其它项目引用,而不能独立运行,只有之前的二进制`package`才可以运行。
与`src/main.js`一样Cargo知道如果一个`package`包含有`src/lib.js`,意味它包含有一个库类型的同名包`my-lib`,该包的根文件是`src/lib.js`
#### 易混淆的package和包
看完上面,相信大家看出来为何`package`和包容易被混淆了吧?因为你用`cargo new`创建的`package`和它其中包含的包是同名的!
不过,只要你牢记`package`是一个项目工程,而包只是一个编译单元,基本上也就不会混淆这个两个概念了: `src/main.rs`和`src/lib.rs`都是编译单元,因此它们都是包。
#### 典型的`package`结构
上面创建的`package`中仅包含`src/main.rs`文件,意味着它仅包含一个二进制同名包`my-project`。如果一个`package`同时拥有`src/main.js`和`src/lib.rs`,那就意味着它包含两个包:库包和二进制包,这两个包名也都是`my-project` —— 都与`package`同名。
一个真实项目中典型的`package`,会包含多个二进制包,这些包文件被放在`src/bin`目录下,每一个文件都是独立的二进制包,同时也会包含一个库包,该包只能存在一个`src/lib.rs`
```css
.
├── Cargo.toml
├── Cargo.lock
├── src
│ ├── main.rs
│ ├── lib.rs
│ └── bin
│ └── main1.rs
│ └── main2.rs
├── tests
│ └── some_integration_tests.rs
├── benches
│ └── simple_bench.rs
└── examples
└── simple_example.rs
```
- 唯一库包, `src/lib.rs`
- 默认二进制包, `src/main.rs`,编译后生成的可执行文件与`package`同名
- 其余二进制包,`src/bin/main1.rs`和`src/bin/main2.rs`,它们会分别生成一个文件同名的二进制可执行文件
- 集成测试文件:`tests`目录下
- 性能测试benchmark文件: `benches`目录下
- 项目示例: `examples`目录下
这种目录结构基本上是Rust的标准目录结构在`github`的大多数项目上,你都将看到它的身影。
理解了包的概念,我们再来看看构成包的基本单元:模块。

@ -0,0 +1,16 @@
# 包和模块
当工程规模变大时,把代码写到一个甚至几个文件中,都是不太聪明的做法,可能存在以下问题:
1. 单个文件过大,导致打开、翻页速度大幅变慢
2. 查询和定位效率大幅降低,类比下,你会把所有知识内容放在一个几十万字的文档中吗?
3. 只有一个代码层次:函数,难以维护和协作,想象一下你的操作系统只有一个根目录,剩下的都是单层子目录会如何: `disaster`
4. 容易滋生Bug
同时,将大的代码文件拆分成包和模块,还允许我们实现代码抽象和复用:将你的代码封装好后提供给用户,那么用户只需要调用公共接口即可,无需知道内部该如何实现。
因此跟其它语言一样Rust也提供了相应概念用于代码的组织管理:
- Packages: 一个`Cargo`提供的feature可以用来构建、测试和分享包
- 包Crate: 一个由多个模块组成的树形结构,可以作为三方库进行分发,也可以生成可执行文件进行运行
- 模块:可以一个文件多个模块,也可以一个文件一个模块,模块可以被认为是真实项目中的代码组织单元
下面,让我们一一来学习这些概念以及如何在实践中运用。

@ -287,11 +287,11 @@ fn main() {
#### 进制
可以使用`#`号来控制数字的进制输出:
- #b, 二进制
- #o, 八进制
- #x, 小写十六进制
- #X, 大写十六进制
- x, 不带前缀的小写十六进制
- `#b`, 二进制
- `#o`, 八进制
- `#x`, 小写十六进制
- `#X`, 大写十六进制
- `x`, 不带前缀的小写十六进制
```rust
fn main() {

@ -0,0 +1 @@
# 文档注释 todo

@ -0,0 +1,3 @@
# 那些会导致UB的代码
https://www.reddit.com/r/rust/comments/rp44u5/request_for_unsafe_review/

Binary file not shown.
Loading…
Cancel
Save