├── styles ├── epub.css ├── website.css └── pdf.css ├── .gitattributes ├── LICENSE.md ├── .gitignore ├── .editorconfig ├── book.json ├── content ├── Learn Rust 学习 Rust.md ├── Effective Rust 高效 Rust.md ├── Syntax and Semantics 语法和语义.md ├── Intrinsics 固有功能.md ├── Slice patterns 切片模式.md ├── Attributes 属性.md ├── Unsized Types 不定长类型.md ├── README 介绍.md ├── Using Rust Without the Standard Library 不使用标准库开发 Rust.md ├── `type` Aliases `type`别名.md ├── Associated Constants 关联常量.md ├── Drop.md ├── If If语句.md ├── Release Channels 发布途径.md ├── Comments 注释.md ├── if let.md ├── `const` and `static`.md ├── Glossary 词汇表.md ├── Conditional Compilation 条件编译.md ├── Box Syntax and Patterns 装箱语法和模式.md ├── Borrow and AsRef Borrow 和 AsRef.md ├── Enums 枚举.md ├── Match 匹配.md ├── Lang items 语言项.md ├── Nightly Rust Rust 开发版.md ├── Raw Pointers 裸指针.md ├── `Deref` coercions `Deref`强制多态.md ├── Universal Function Call Syntax 通用函数调用语法.md ├── Operators and Overloading 运算符和重载.md ├── `unsafe` 不安全代码.md ├── Vectors.md ├── Advanced Linking 链接进阶.md ├── Benchmark tests 基准测试.md ├── Mutability 可变性.md ├── No stdlib 不使用标准库.md ├── Loops 循环.md ├── Generics 泛型.md ├── Strings 字符串.md ├── Inline Assembly 内联汇编.md ├── Casting Between Types 类型转换.md ├── Associated Types 关联类型.md ├── Structs 结构体.md ├── Custom Allocators 自定义内存分配器.md ├── Method Syntax 方法语法.md ├── Ownership 所有权.md ├── Variable Bindings 变量绑定.md ├── Bibliography 参考文献.md ├── Primitive Types 原生类型.md ├── Procedural Macros 过程宏.md ├── Rust Inside Other Languages 其它语言中的 Rust.md ├── Functions 函数.md ├── Iterators 迭代器.md ├── Patterns 模式.md ├── Trait Objects trait 对象.md ├── Compiler Plugins 编译器插件.md ├── Choosing your Guarantees 选择你的保证.md ├── References and Borrowing 引用和借用.md └── Lifetimes 生命周期.md ├── preface.md ├── 名词中英文对照.md ├── README.md ├── CONTRIBUTING.md └── SUMMARY.md /styles/epub.css: -------------------------------------------------------------------------------- 1 | /* CSS for epub */ 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.md linguist-detectable=true 2 | -------------------------------------------------------------------------------- /styles/website.css: -------------------------------------------------------------------------------- 1 | /* CSS for website */ 2 | div.book-summary nav ul li a b {display:none;} -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | [![相同方式共享 4.0](https://i.creativecommons.org/l/by-sa/4.0/88x31.png "相同方式共享 4.0")](http://creativecommons.org/licenses/by-sa/4.0/) 2 | 3 | 本作品采用 [知识共享署名 - 相同方式共享 4.0 国际许可协议](https://creativecommons.org/licenses/by-sa/4.0/) 进行许可。 4 | -------------------------------------------------------------------------------- /styles/pdf.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: serif; 3 | } 4 | 5 | pre { 6 | word-break: break-all; 7 | /* Non standard for webkit, but it does work on the Mac when generating the PDF */ 8 | word-break: break-word; 9 | } 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Dependency directory 2 | ## Commenting this out is preferred by some people, see 3 | ## https://docs.npmjs.com/misc/faq#should-i-check-my-node_modules-folder-into-git 4 | node_modules 5 | 6 | # Book build output 7 | _book 8 | 9 | *.log 10 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | indent_style = space 6 | end_of_line = lf 7 | charset = utf-8 8 | indent_size = 2 9 | trim_trailing_whitespace = true 10 | insert_final_newline = true 11 | 12 | [*.md] 13 | trim_trailing_whitespace = false 14 | insert_final_newline = false 15 | -------------------------------------------------------------------------------- /book.json: -------------------------------------------------------------------------------- 1 | { 2 | "gitbook": ">=2.0.0", 3 | 4 | "title": "rust 官方教程 中文版", 5 | 6 | "language": "zh-hans", 7 | 8 | "structure": { 9 | "readme": "preface.md" 10 | }, 11 | 12 | "styles": { 13 | "pdf": "styles/pdf.css" 14 | }, 15 | 16 | "plugins": [], 17 | 18 | "pluginsConfig": {} 19 | } 20 | -------------------------------------------------------------------------------- /content/Learn Rust 学习 Rust.md: -------------------------------------------------------------------------------- 1 | # 学习 Rust 2 | 3 | > [learn-rust.md](https://github.com/rust-lang/rust/blob/stable/src/doc/book/learn-rust.md) 4 | >
5 | > commit 024aa9a345e92aa1926517c4d9b16bd83e74c10d 6 | 7 | 欢迎!这一部分有一些通过构建项目来教你学会使用 Rust 的教程。你会学到一个高层次的理解,不过我们会略过细节。 8 | 9 | 如果你更喜欢一个“自底向上”风格的经验,查看[语法和语义](Syntax and Semantics 语法和语义.md)。 10 | -------------------------------------------------------------------------------- /content/Effective Rust 高效 Rust.md: -------------------------------------------------------------------------------- 1 | # 高效 Rust 2 | 3 | > [effective-rust.md](https://github.com/rust-lang/book/blob/master/first-edition/src/effective-rust.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | 那么你已经学会了如何写一些 Rust 代码了。不过能写**一些** Rust 代码和能写**好** Rust 代码还是有区别的。 8 | 9 | 这个部分包含一些相对独立的教程,它们向你展示如何将你的 Rust 带入下一个等级。常见模式和标准库功能将被介绍。你可以选择任意顺序阅读这一部分。 10 | -------------------------------------------------------------------------------- /content/Syntax and Semantics 语法和语义.md: -------------------------------------------------------------------------------- 1 | # 语法和语义 2 | 3 | > [syntax-and-semantics.md](https://github.com/rust-lang/book/blob/master/first-edition/src/syntax-and-semantics.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | 这一部分将 Rust 拆成小的部分,每一部分对应一个概念。 8 | 9 | 如果你想要自底向上的学习 Rust,按顺序阅读这一部分将会有很大帮助。 10 | 11 | 这些部分也组成了一个对各种概念的参考,所以如果你阅读其它教程并发现一些迷惑的问题,你可以在这里找到一些解释。 12 | -------------------------------------------------------------------------------- /content/Intrinsics 固有功能.md: -------------------------------------------------------------------------------- 1 | # 固有功能 2 | 3 | > [intrinsics.md](https://github.com/rust-lang/rust/blob/stable/src/doc/book/intrinsics.md) 4 | >
5 | > commit 024aa9a345e92aa1926517c4d9b16bd83e74c10d 6 | 7 | > **注意**:固有功能将会永远是一个不稳定的接口,推荐使用稳定的 libcore 接口而不是直接使用编译器自带的功能。 8 | 9 | 可以像 FFI 函数那样导入它们,使用特殊的`rust-intrinsic`ABI。例如,如果在一个独立的上下文,但是想要能在类型间`transmute`,并想进行高效的指针计算,你可以声明函数: 10 | 11 | ```rust 12 | #![feature(intrinsics)] 13 | # fn main() {} 14 | 15 | extern "rust-intrinsic" { 16 | fn transmute(x: T) -> U; 17 | 18 | fn offset(dst: *const T, offset: isize) -> *const T; 19 | } 20 | ``` 21 | 22 | 跟其它 FFI 函数一样,它们总是`unsafe`的。 23 | -------------------------------------------------------------------------------- /preface.md: -------------------------------------------------------------------------------- 1 | [![相同方式共享 4.0](https://i.creativecommons.org/l/by-sa/4.0/88x31.png "相同方式共享 4.0")](http://creativecommons.org/licenses/by-sa/4.0/) 2 | 3 | 本作品采用 [知识共享署名 - 相同方式共享 4.0 国际许可协议](https://creativecommons.org/licenses/by-sa/4.0/) 进行许可。 4 | 5 | # Rust 程序设计语言(第一版) 简体中文版 6 | 本文档为 [*The Rust Programming Language*](https://doc.rust-lang.org/book/) 的中文翻译。 7 | 欢迎在 [GitHub](https://github.com/KaiserY/rust-book-chinese) 上为本文档做出贡献。 8 | 9 | ## 关于版本 10 | 11 | 本书是 Rust 程序设计语言(第一版)的翻译 12 | 13 | Rust 程序设计语言(第二版)还在施工中,不过大体已经完成。 14 | 15 | Rust 程序设计语言(第二版)官方仓库:[https://github.com/rust-lang/book](https://github.com/rust-lang/book) 16 | 17 | Rust 程序设计语言(第二版)翻译仓库:[https://github.com/KaiserY/trpl-zh-cn](https://github.com/KaiserY/trpl-zh-cn) 18 | 19 | Rust 程序设计语言(第二版)翻译阅读:[https://kaisery.github.io/trpl-zh-cn](https://kaisery.github.io/trpl-zh-cn) 20 | 21 | ## Rust 版本 22 | 1.17.0 23 | 24 | ## 相关资源 25 | * GitHub: https://github.com/KaiserY/rust-book-chinese 26 | * GitBook: https://www.gitbook.com/book/kaisery/rust-book-chinese 27 | * Rust 中文社区: https://rust-china.org 28 | * QQ 群: 144605258 29 | * QQ 2 群: 303838735 30 | * 聊天频道:https://chat.rust-china.org/ 31 | -------------------------------------------------------------------------------- /content/Slice patterns 切片模式.md: -------------------------------------------------------------------------------- 1 | # 切片模式 2 | 3 | > [slice-patterns.md](https://github.com/rust-lang/rust/blob/stable/src/doc/book/slice-patterns.md) 4 | >
5 | > commit 5cf4139d21073731fa7f7226d941349dbacc16d6 6 | 7 | 如果你想在一个切片或数组上匹配,你可以通过`slice_patterns`功能使用`&`: 8 | 9 | ```rust 10 | #![feature(slice_patterns)] 11 | 12 | fn main() { 13 | let v = vec!["match_this", "1"]; 14 | 15 | match &v[..] { 16 | &["match_this", second] => println!("The second element is {}", second), 17 | _ => {}, 18 | } 19 | } 20 | ``` 21 | 22 | `advanced_slice_patterns`gate 让你使用`..`表明在一个切片的模式匹配中任意数量的元素。这个通配符对一个给定的数组只能只用一次。如果在`..`之前有一个标识符,结果会被绑定到那个名字上。例如: 23 | 24 | ```rust 25 | #![feature(advanced_slice_patterns, slice_patterns)] 26 | 27 | fn is_symmetric(list: &[u32]) -> bool { 28 | match list { 29 | &[] | &[_] => true, 30 | &[x, ref inside.., y] if x == y => is_symmetric(inside), 31 | _ => false 32 | } 33 | } 34 | 35 | fn main() { 36 | let sym = &[0, 1, 4, 2, 4, 1, 0]; 37 | assert!(is_symmetric(sym)); 38 | 39 | let not_sym = &[0, 1, 7, 2, 4, 1, 0]; 40 | assert!(!is_symmetric(not_sym)); 41 | } 42 | ``` 43 | -------------------------------------------------------------------------------- /content/Attributes 属性.md: -------------------------------------------------------------------------------- 1 | # 属性 2 | 3 | > [attributes.md](https://github.com/rust-lang/book/blob/master/first-edition/src/attributes.md) 4 | >
5 | > commit bd8d27beb54ef2a7bb4162d43006792f9ceae361 6 | 7 | 在 Rust 中声明可以用“属性”标注,它们看起来像: 8 | 9 | ```rust 10 | #[test] 11 | # fn foo() {} 12 | ``` 13 | 14 | 或像这样: 15 | 16 | ```rust 17 | # mod foo { 18 | #![test] 19 | # } 20 | ``` 21 | 22 | 这两者的区别是`!`,它改变了属性作用的对象: 23 | 24 | ```rust 25 | #[foo] 26 | struct Foo; 27 | 28 | mod bar { 29 | #![bar] 30 | } 31 | ``` 32 | 33 | `#[foo]`作用于下一个项,在这就是`struct`声明。`#![bar]`作用于包含它的项,在这是`mod`声明。否则,它们是一样的。它们都以某种方式改变它们附加到的项的意义。 34 | 35 | 例如,考虑一个像这样的函数: 36 | 37 | ```rust 38 | #[test] 39 | fn check() { 40 | assert_eq!(2, 1 + 1); 41 | } 42 | ``` 43 | 44 | 它被标记为`#[test]`。这意味着它是特殊的:当你运行[测试](Testing 测试.md),这个函数将会执行。当你正常编译时,它甚至不会被包含进来。这个函数现在是一个测试函数。 45 | 46 | 属性也可以有附加数据: 47 | 48 | ```rust 49 | #[inline(always)] 50 | fn super_fast_fn() { 51 | # } 52 | ``` 53 | 54 | 或者甚至是键值: 55 | 56 | ```rust 57 | #[cfg(target_os = "macos")] 58 | mod macos_only { 59 | # } 60 | ``` 61 | 62 | Rust属性被用在一系列不同的地方。在[参考手册](http://doc.rust-lang.org/nightly/reference.html#attributes)中有一个属性的全表。目前,你不能创建你自己的属性,Rust 编译器定义了它们。 63 | -------------------------------------------------------------------------------- /名词中英文对照.md: -------------------------------------------------------------------------------- 1 | # 名词中英文对照 2 | 3 | > 这里收集了一些 Rust 的名词翻译,并在此统一说明。如果你在阅读时遇到无法理解地方,请提 issue。 4 | 5 | English name | 中文名 | 备注 6 | ------------------- |------------ |------ 7 | attribute | 特性 | 8 | coercion | 强制转换 | 9 | composition | 组合 | 10 | deref coercions | 解引用强制多态 | 11 | expansion / expand | 展开 | 12 | extension / extend | 扩展 | 13 | free variables | 自由变量 | 14 | idempotent | 幂等 | 15 | noalias | *不译* | http://llvm.org/docs/LangRef.html#noalias 16 | aliasing | 重叠 | http://llvm.org/docs/LangRef.html#pointer-aliasing-rules 17 | slice | *不译* | `slice`不做翻译,文档中的“切片”都是`slice`的意思。 18 | synchronous types | 同步类型 19 | trait | *不译* | `trait`不做翻译 20 | trait object | trait 对象 | 21 | undefined behavior | 未定义行为 | 22 | unsafe | 不安全 | 23 | vector | *不译* | 鉴于将`vector`翻译为`向量`容易引起误解,故决定不再对其进行翻译,如果你在本书中看到“向量”一词,这一定是还未修改过来,请自行脑补为`vector` 24 | -------------------------------------------------------------------------------- /content/Unsized Types 不定长类型.md: -------------------------------------------------------------------------------- 1 | # 不定长类型 2 | 3 | > [unsized-types.md](https://github.com/rust-lang/book/blob/master/first-edition/src/unsized-types.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | 大部分类型有一个特定的大小,以字节为单位,它们在编译时是已知的。例如,一个`i32`是 32 位大,或者 4 个字节。然而,有些类型有益于表达,却没有一个定义的大小。它们叫做“不定长”或者“动态大小”类型。一个例子是`[T]`。这个类型代表一个特定数量`t`的序列。不过我们并不知道有多少,所以大小是未知的。 8 | 9 | Rust 知道几个这样的类型,不过它们有一些限制。这有三个: 10 | 11 | 1. 我们只能通过指针操作一个不定长类型的实例。`&[T]`刚好能正常工作,不过`[T]`不行。一个` &[T]`能正常工作,不过一个`[T]`不行。 12 | 2. 变量和参数不能拥有动态大小类型。 13 | 3. 只有一个`struct`的最后一个字段可能拥有一个动态大小类型;其它字段则不可以拥有动态大小类型。枚举变量不可以用动态大小类型作为数据。 14 | 15 | 所以为什么这很重要?好吧,因为`[T]`只能用在一个指针之后,如果我们没有对不定长类型的语言支持,它将不可能这么写: 16 | 17 | ```rust 18 | impl Foo for str { 19 | ``` 20 | 21 | 或者 22 | 23 | ```rust 24 | impl Foo for [T] { 25 | ``` 26 | 27 | 相反,你将不得不这么写: 28 | 29 | ```rust 30 | impl Foo for &str { 31 | ``` 32 | 33 | 意味深长的是,这个实现将只能用于[引用](References and Borrowing 引用和借用.md),并且不能用于其它类型的指针。通过`impl for str`,所有指针,包括(在一些地方,这里会有bug需要修复)用户自定义的智能指针,可以使用这个`impl`。 34 | 35 | ## `?Sized` 36 | 37 | 如果你想要写一个接受动态大小类型的函数,你可以使用这个特殊的 bound 语法,`?Sized`: 38 | 39 | ```rust 40 | struct Foo { 41 | f: T, 42 | } 43 | ``` 44 | 45 | 这个`?Sized`,读作“`T`可能是`Sized`的”,允许我们匹配固定长度和不定长度的类型。所有泛型类型参数隐式包含`Sized` bound,所以`?Sized`可以被用来移除这个隐式 bound。 46 | -------------------------------------------------------------------------------- /content/README 介绍.md: -------------------------------------------------------------------------------- 1 | # Rust 编程语言 2 | 3 | > [README.md](https://github.com/rust-lang/book/blob/master/first-edition/src/README.md) 4 | >
5 | > commit 8dc59a54391b8ce44b25659c7c1f8dcde3685bff 6 | 7 | 欢迎阅读!这本书将教会你使用[Rust 编程语言][rust]。Rust 是一个系统编程语言,它注重于三个方面:安全,速度和并发性。为了实现这些目标,它没有采用垃圾回收机制(GC)。这让它在其它语言并不擅长的场景中大展身手:嵌入到其它语言中、在特定的时间和空间要求下编程、编写例如设备驱动和操作系统这样的底层代码。它通过一系列不产生运行时开销的编译时安全检查来提升目前语言所关注的领域,同时消除一切数据竞争。Rust 还致力于实现“零开销抽象”,虽然有些抽象看起来更像一个高级语言的特性。即便如此,你仍然可以使用 Rust 来做一些底层的精准控制。 8 | 9 | [rust]: https://www.rust-lang.org 10 | 11 | 《Rust编程语言》分为数章。本介绍是第一章。之后是: 12 | 13 | * [准备][gs] - 为你的电脑安装 Rust 开发环境。 14 | * [教程:猜测游戏][gg] - 通过一个小项目体验 Rust。 15 | * [语法和语义][ss] - Rust 各个部分,被拆分成小的部分讲解。 16 | * [高效 Rust][er] - 编写优秀 Rust 代码的高级内容。 17 | * [Rust 开发版][nr] - 还未出现在稳定版本中的最新功能。(译者注:本章节在现版本 1.17.0 中已经去掉,暂时保留,不具有时效性) 18 | * [词汇表][gl] - 书中使用的术语的参考。 19 | * [参考文献][bi] - 影响过 Rust 的文献,关于 Rust 的论文。 20 | 21 | [gs]: Getting%20Started%20准备.html 22 | [gg]: Guessing%20Game%20猜猜看.html 23 | [er]: Effective%20Rust%20高效%20Rust.html 24 | [ss]: Syntax%20and%20Semantics%20语法和语义.html 25 | [nr]: Nightly%20Rust%20Rust%20开发版.html 26 | [gl]: Glossary%20词汇表.html 27 | [bi]: Bibliography%20参考文献.html 28 | 29 | ## 贡献 30 | 31 | 生成这本书(英文版)的源文件可以在 [GitHub][book] 上找到。 32 | 33 | 34 | [book]: https://github.com/rust-lang/book/tree/master/first-edition/src 35 | -------------------------------------------------------------------------------- /content/Using Rust Without the Standard Library 不使用标准库开发 Rust.md: -------------------------------------------------------------------------------- 1 | # 不使用标准库开发 Rust 2 | 3 | > [no-stdlib.md](https://github.com/rust-lang/book/blob/master/first-edition/src/using-rust-without-the-standard-library.md) 4 | >
5 | > commit 98b18fabddaa942c6bbbb44509b7c00f4b07915f 6 | 7 | Rust 的标准库提供了很多有用的功能,不过它假设它的 host 系统的多种功能的支持:线程,网络,堆分配和其他功能。有些系统并没有这些功能,不过,Rust也能在这些系统上工作。为此,我们可以通过一个属性来告诉 Rust 我们不想使用标准库:`#![no_std]`。 8 | 9 | > 注意:这个功能技术上是稳定的,不过有些附加条件。其一,你可以构建一个稳定的`#![no_std]`库,但二进制文件不行。关于没有标准库的库文件的细节,查看[关于`#![no_std]`的章节](https://github.com/rust-lang/rust/blob/master/src/doc/book/using-rust-without-the-standard-library.html)。 10 | 11 | 为了使用`#![no_std]`,在 crate 的根文件上加入: 12 | 13 | ```rust 14 | #![no_std] 15 | 16 | fn plus_one(x: i32) -> i32 { 17 | x + 1 18 | } 19 | ``` 20 | 21 | 很多暴露于标准库中的功能通过[`core` crate](https://github.com/rust-lang/rust/blob/stable/src/doc/core/index.html)也同样可用。当我们使用标准库时,Rust 自动将`std`引入到作用域中,允许我们不用显示导入就能使用相关功能。相似的,当使用`#![no_std]`,Rust 会将`core`引入作用域中,以及[它的 prelude](https://github.com/rust-lang/rust/blob/stable/src/doc/core/prelude/v1)。这意味着很多代码也是能正常运行的: 22 | 23 | ```rust 24 | #![no_std] 25 | 26 | fn may_fail(failure: bool) -> Result<(), &'static str> { 27 | if failure { 28 | Err("this didn’t work!") 29 | } else { 30 | Ok(()) 31 | } 32 | } 33 | ``` 34 | -------------------------------------------------------------------------------- /content/`type` Aliases `type`别名.md: -------------------------------------------------------------------------------- 1 | # type 别名 2 | 3 | > [type-aliases.md](https://github.com/rust-lang/book/blob/master/first-edition/src/type-aliases.md) 4 | >
5 | > commit 23a7a7bdb6a6a43cd7efdd9176b1d3f75d9d0e70 6 | 7 | `type`关键字让你定义另一个类型的别名: 8 | 9 | ```rust 10 | type Name = String; 11 | ``` 12 | 13 | 你可以像一个真正类型那样使用这个类型: 14 | 15 | ```rust 16 | type Name = String; 17 | 18 | let x: Name = "Hello".to_string(); 19 | ``` 20 | 21 | 然而要注意的是,这一个**别名**,完全不是一个新的类型。换句话说,因为 Rust 是强类型的,你可以预期两个不同类型的比较会失败: 22 | 23 | ```rust 24 | let x: i32 = 5; 25 | let y: i64 = 5; 26 | 27 | if x == y { 28 | // ... 29 | } 30 | ``` 31 | 32 | 这给出 33 | 34 | ```text 35 | error: mismatched types: 36 | expected `i32`, 37 | found `i64` 38 | (expected i32, 39 | found i64) [E0308] 40 | if x == y { 41 | ^ 42 | ``` 43 | 44 | 不过,如果我们有一个别名: 45 | 46 | ```rust 47 | type Num = i32; 48 | 49 | let x: i32 = 5; 50 | let y: Num = 5; 51 | 52 | if x == y { 53 | // ... 54 | } 55 | ``` 56 | 57 | 这会无错误的编译。从任何角度来说,`Num`类型的值与`i32`类型的值都是一样的。 58 | 59 | 你也可以在泛型中使用类型别名: 60 | 61 | ```rust 62 | use std::result; 63 | 64 | enum ConcreteError { 65 | Foo, 66 | Bar, 67 | } 68 | 69 | type Result = result::Result; 70 | ``` 71 | 72 | 这创建了一个特定版本的`Result`类型,它总是有一个`ConcreteError`作为`Result`的`E`那部分。这通常用于标准库中创建每个子部分的自定义错误。例如,[`io::Result`](http://doc.rust-lang.org/nightly/std/io/type.Result.html)。 73 | -------------------------------------------------------------------------------- /content/Associated Constants 关联常量.md: -------------------------------------------------------------------------------- 1 | # 关联常量 2 | 3 | > [associated-constants.md](https://github.com/rust-lang/rust/blob/stable/src/doc/book/associated-constants.md) 4 | >
5 | > commit 024aa9a345e92aa1926517c4d9b16bd83e74c10d 6 | 7 | 通过`associated_consts`功能,你像这样可以定义常量: 8 | 9 | ```rust 10 | #![feature(associated_consts)] 11 | 12 | trait Foo { 13 | const ID: i32; 14 | } 15 | 16 | impl Foo for i32 { 17 | const ID: i32 = 1; 18 | } 19 | 20 | fn main() { 21 | assert_eq!(1, i32::ID); 22 | } 23 | ``` 24 | 25 | 任何`Foo`的定义都必须定义`ID`,不定义的话: 26 | 27 | ```rust 28 | #![feature(associated_consts)] 29 | 30 | trait Foo { 31 | const ID: i32; 32 | } 33 | 34 | impl Foo for i32 { 35 | } 36 | ``` 37 | 38 | 会给出 39 | 40 | ```text 41 | error: not all trait items implemented, missing: `ID` [E0046] 42 | impl Foo for i32 { 43 | } 44 | ``` 45 | 46 | 也可以实现一个默认值: 47 | 48 | ```rust 49 | #![feature(associated_consts)] 50 | 51 | trait Foo { 52 | const ID: i32 = 1; 53 | } 54 | 55 | impl Foo for i32 { 56 | } 57 | 58 | impl Foo for i64 { 59 | const ID: i32 = 5; 60 | } 61 | 62 | fn main() { 63 | assert_eq!(1, i32::ID); 64 | assert_eq!(5, i64::ID); 65 | } 66 | ``` 67 | 68 | 如你所见,当实现`Foo`时,你可以不实现它(关联常量),当作为`i32`时。接着它将会使用默认值。不过,作为`i64`时,我们可以增加我们自己的定义。 69 | 70 | 关联常量并不一定要关联在一个 trait 上。一个`struct`的`impl`块或`enum`也行: 71 | 72 | ```rust 73 | #![feature(associated_consts)] 74 | 75 | struct Foo; 76 | 77 | impl Foo { 78 | const FOO: u32 = 3; 79 | } 80 | ``` 81 | -------------------------------------------------------------------------------- /content/Drop.md: -------------------------------------------------------------------------------- 1 | # `Drop` 2 | 3 | > [drop.md](https://github.com/rust-lang/book/blob/master/first-edition/src/drop.md) 4 | >
5 | > commit bd8d27beb54ef2a7bb4162d43006792f9ceae361 6 | 7 | 现在我们讨论了 trait,让我们看看一个由 Rust 标准库提供的特殊 trait,[`Drop`](http://doc.rust-lang.org/nightly/std/ops/trait.Drop.html)。`Drop` trait 提供了一个当一个值离开作用域后运行一些代码的方法。例如: 8 | 9 | ```rust 10 | struct HasDrop; 11 | 12 | impl Drop for HasDrop { 13 | fn drop(&mut self) { 14 | println!("Dropping!"); 15 | } 16 | } 17 | 18 | fn main() { 19 | let x = HasDrop; 20 | 21 | // Do stuff. 22 | 23 | } // `x` goes out of scope here. 24 | ``` 25 | 26 | 当在` main()`的末尾`x`离开作用域的时候,`Drop`的代码将会执行。`Drop`有一个方法,他也叫做`drop()`。它获取一个`self`的可变引用。 27 | 28 | 就是这样!`Drop`的机制非常简单,不过这有一些细节。例如,值会以与它们声明相反的顺序被丢弃(dropped)。这是另一个例子: 29 | 30 | ```rust 31 | struct Firework { 32 | strength: i32, 33 | } 34 | 35 | impl Drop for Firework { 36 | fn drop(&mut self) { 37 | println!("BOOM times {}!!!", self.strength); 38 | } 39 | } 40 | 41 | fn main() { 42 | let firecracker = Firework { strength: 1 }; 43 | let tnt = Firework { strength: 100 }; 44 | } 45 | ``` 46 | 47 | 这会输出: 48 | 49 | ```text 50 | BOOM times 100!!! 51 | BOOM times 1!!! 52 | ``` 53 | 54 | `tnt`在`firecracker`之前离开作用域(原文大意:TNT 在爆竹之前爆炸),因为它在之后被声明。后进先出。 55 | 56 | 那么`Drop`有什么好处呢?通常来说,`Drop`用来清理任何与`struct`关联的资源。例如,[`Arc`类型](http://doc.rust-lang.org/nightly/std/sync/struct.Arc.html)是一个引用计数类型。当`Drop`被调用,它会减少引用计数,并且如果引用的总数为0,将会清除底层的值。 57 | -------------------------------------------------------------------------------- /content/If If语句.md: -------------------------------------------------------------------------------- 1 | # If 2 | 3 | > [if.md](https://github.com/rust-lang/book/blob/master/first-edition/src/if.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | Rust 的 `if` 并不是特别复杂,如果您接触过其他的编程语言,掌握'if'并不复杂。不过你会发现它更像动态类型语言而不是更传统的系统语言。所以让我来说说这些不同于其他语言的细节,以便你能把握这些细节。 8 | 9 | `if` 语句是**分支**这个更加宽泛的概念的一个特定形式。它的名字来源于树的树枝:一个选择点,根据选择的不同,将会使用不同的路径。 10 | 11 | `if`语句常见有三种情况: 12 | (1)if:在 `if` 语句中,有一个引向两条路径的选择: 13 | 14 | ```rust 15 | let x = 5; 16 | 17 | if x == 5 { 18 | println!("x is five!"); 19 | } 20 | ``` 21 | 22 | 如果在什么别的地方更改了`x`的值,这一行将不会输出。更具体一点,如果`if`后面的表达式的值为`true`,这个代码块将被执行。为`false`则不被执行。 23 | 24 | (2)if-else:如果你想当值为`false`时执行些什么,使用`else`: 25 | 26 | ```rust 27 | let x = 5; 28 | 29 | if x == 5 { 30 | println!("x is five!"); 31 | } else { 32 | println!("x is not five :("); 33 | } 34 | ``` 35 | 36 | (3)if-else if - else:如果不止一种情况,使用`else if`: 37 | 38 | ```rust 39 | let x = 5; 40 | 41 | if x == 5 { 42 | println!("x is five!"); 43 | } else if x == 6 { 44 | println!("x is six!"); 45 | } else { 46 | println!("x is not five or six :("); 47 | } 48 | ``` 49 | 50 | 这些都是非常标准的情况。然而你也可以这么写: 51 | 52 | ```rust 53 | let x = 5; 54 | 55 | let y = if x == 5 { 56 | 10 57 | } else { 58 | 15 59 | }; // y: i32 60 | ``` 61 | 62 | 你可以(或许也应该)这么写: 63 | 64 | ```rust 65 | let x = 5; 66 | 67 | let y = if x == 5 { 10 } else { 15 }; // y: i32 68 | ``` 69 | 70 | 这代码可以被执行是因为`if`是一个表达式。表达式的值是任何被选择的分支的最后一个表达式的值。一个没有`else`的`if`总是返回`()`作为返回值。 71 | -------------------------------------------------------------------------------- /content/Release Channels 发布途径.md: -------------------------------------------------------------------------------- 1 | # 发布途径 2 | 3 | > [release-channels.md](https://github.com/rust-lang/book/blob/master/first-edition/src/release-channels.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | Rust 项目使用一个叫做“发布途径”的概念来管理发布。理解这个选择你的项目应该使用哪个版本的 Rust 的过程是很重要的。 8 | 9 | ## 概览 10 | 11 | Rust 发布有 3 种途径: 12 | 13 | * 开发版(Nightly) 14 | * 测试版(Beta) 15 | * 稳定版(Stable) 16 | 17 | 新的开发发布每天创建一次。每 6 个星期,最后的开发版被提升为“测试版”。在这时,它将只会收到修改重大错误的补丁。6 个星期之后,测试版被提升为“稳定版”,而成为下一个版本的`1.x`发布。 18 | 19 | 这个过程并行发生。所以每 6 个星期,在同一天,开发变测试,测试变稳定。当`1.x`发布时的同时,`1.(x + 1)-beta`被发布,而开发版变为第一版的`1.(x + 2)-nightly`。 20 | 21 | ## 选择一个版本 22 | 23 | 通常来说,除非你有一个特定的原因,你应该使用稳定发布途径。这个发布意为用于普通用户。 24 | 25 | 然而,根据你对Rust的兴趣,你可能会选择使用开发构建。基本的权衡是:在开发途径,你可以使用不稳定的,新的Rust功能。然而,不稳定功能倾向于改变,所以任何新的开发版发布可能会破坏你的代码。如果你使用稳定发布,你不能使用实验功能,不过下一个Rust发布将不会因为破环性改变造成显著的问题。 26 | 27 | ## 通过持续集成(CI)改善生态系统 28 | 29 | 那么测试版怎么样呢?我们鼓励所有使用稳定发布途径的 Rust 用户在他们的持续集成系统中也针对测试途径进行测试。这会帮助警告团队以防出现一个意外的退步(regression)。 30 | 31 | 另外,针对开发版测试能够更快的捕获退步,因此如果你不介意一个第三种构建(环境),我们也会感激你针对开发版进行测试。 32 | 33 | 作为一个例子,很多 Rust 程序猿使用[Travis](https://travis-ci.org/)来测试他们的 crate,这是一个开源的免费项目。Travis[直接支持Rust](http://docs.travis-ci.com/user/languages/rust/),并且你可以用类似这样的一个`.travis.yml`文件来测试所有的版本: 34 | 35 | ```yaml 36 | language: rust 37 | rust: 38 | - nightly 39 | - beta 40 | - stable 41 | 42 | matrix: 43 | allow_failures: 44 | - rust: nightly 45 | ``` 46 | 47 | 通过这个配置,Travis 将会测试所有三个版本,不过如果有什么东西在开发版中失败了,你的构建将不会失败。建议你在任何 CI 系统中使用类似的配置,查看你正在使用的 CI 系统的文档来获取更多细节。 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![相同方式共享 4.0](https://i.creativecommons.org/l/by-sa/4.0/88x31.png "相同方式共享 4.0")](http://creativecommons.org/licenses/by-sa/4.0/) 2 | 3 | 本作品采用 [知识共享署名 - 相同方式共享 4.0 国际许可协议](https://creativecommons.org/licenses/by-sa/4.0/) 进行许可。 4 | 5 | # Rust 程序设计语言(第一版) 简体中文版 6 | 本文档为 [*The Rust Programming Language*](https://doc.rust-lang.org/book/) 的中文翻译。 7 | 欢迎为本翻译文档做出贡献。 8 | 9 | ## 关于版本 10 | 11 | 本书是 Rust 程序设计语言(第一版)的翻译 12 | 13 | Rust 程序设计语言(第二版)还在施工中,不过大体已经完成。 14 | 15 | Rust 程序设计语言(第二版)官方仓库:[https://github.com/rust-lang/book](https://github.com/rust-lang/book) 16 | 17 | Rust 程序设计语言(第二版)翻译仓库:[https://github.com/KaiserY/trpl-zh-cn](https://github.com/KaiserY/trpl-zh-cn) 18 | 19 | Rust 程序设计语言(第二版)翻译阅读:[https://kaisery.github.io/trpl-zh-cn](https://kaisery.github.io/trpl-zh-cn) 20 | 21 | Rust 程序设计语言(第二版)翻译阅读:[上海站点加速查看站点http://rustdoc.saigao.fun](http://rustdoc.saigao.fun) 22 | 23 | ## Rust 版本 24 | 1.17.0 25 | 26 | ## 国内镜像 27 | 请参考[中文 Wiki](https://wiki.rust-china.org/%E5%9B%BD%E5%86%85%E9%95%9C%E5%83%8F)上的说明 28 | 29 | ## 电子书 30 | * **[GitBook.com](https://www.gitbook.com/book/kaisery/rust-book-chinese)上的电子书中文字体已有所改善,请优先去这里下载:)** 31 | * 请使用 [GitBook](https://github.com/GitbookIO/gitbook) 来生成电子书。 32 | 33 | ## 相关资源 34 | * 本文档 GitBook: https://www.gitbook.com/book/kaisery/rust-book-chinese 35 | * Rust 中文社区: https://rust-china.org 36 | * Rust 中文 Wiki: https://wiki.rust-china.org 37 | * QQ 群: 144605258 38 | * QQ 2 群: 303838735 39 | * 聊天频道:https://chat.rust-china.org/ 40 | * 想要为 Rust 社区贡献文档的,可以到[RustPrimer](https://github.com/rustcc/RustPrimer)项目里看看 41 | 42 | ## 贡献者 43 | [贡献者](CONTRIBUTING.md) 44 | -------------------------------------------------------------------------------- /content/Comments 注释.md: -------------------------------------------------------------------------------- 1 | # 注释 2 | 3 | > [comments.md](https://github.com/rust-lang/book/blob/master/first-edition/src/comments.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | 现在我们写了一些函数,是时候学习一下注释了。注释是你帮助其他程序员理解你的代码的备注。编译器基本上会忽略它们。 8 | 9 | Rust 有两种需要你了解的注释格式:**行注释**(*line comments*)和**文档注释**(*doc comments*)。 10 | 11 | ```rust 12 | // Line comments are anything after ‘//’ and extend to the end of the line. 13 | 14 | let x = 5; // This is also a line comment. 15 | 16 | // If you have a long explanation for something, you can put line comments next 17 | // to each other. Put a space between the // and your comment so that it’s 18 | // more readable. 19 | ``` 20 | 21 | 另一种注释是文档注释。文档注释使用`///`而不是`//`,并且内建 Markdown 标记支持: 22 | 23 | ~~~rust 24 | /// Adds one to the number given. 25 | /// 26 | /// # Examples 27 | /// 28 | /// ``` 29 | /// let five = 5; 30 | /// 31 | /// assert_eq!(6, add_one(5)); 32 | /// # fn add_one(x: i32) -> i32 { 33 | /// # x + 1 34 | /// # } 35 | /// ``` 36 | fn add_one(x: i32) -> i32 { 37 | x + 1 38 | } 39 | ~~~ 40 | 41 | 有另外一种风格的文档注释,`//!`,用来注释包含它的项(也就是说,crate,模块或者函数),而不是位于它之后的项。它经常用在 crate 根文件(lib.rs)或者模块根文件(mod.rs): 42 | 43 | ```rust 44 | //! # The Rust Standard Library 45 | //! 46 | //! The Rust Standard Library provides the essential runtime 47 | //! functionality for building portable Rust software. 48 | ``` 49 | 50 | 当书写文档注释时,加上参数和返回值部分并提供一些用例将是非常,非常有帮助的。你会注意到我们在这里用了一个新的宏:`assert_eq!`。它比较两个值,并当它们不相等时`panic!`。这在文档中是非常有帮助的。还有一个宏,`assert!`,它在传递给它的值是`false`的时候`panic!`。 51 | 52 | 你可以使用[rustdoc](4.4.Documentation 文档.md)工具来将文档注释生成为 HTML 文档,也可以将代码示例作为测试运行! 53 | -------------------------------------------------------------------------------- /content/if let.md: -------------------------------------------------------------------------------- 1 | # if let 2 | 3 | > [if-let.md](https://github.com/rust-lang/book/blob/master/first-edition/src/if-let.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | `if let`允许你合并`if`和`let`来减少特定类型模式匹配的开销。 8 | 9 | 例如,让我们假设我们有一些`Option`。我们想让它是`Some`时在其上调用一个函数,而它是`None`时什么也不做。这看起来像: 10 | 11 | ```rust 12 | # let option = Some(5); 13 | # fn foo(x: i32) { } 14 | match option { 15 | Some(x) => { foo(x) }, 16 | None => {}, 17 | } 18 | ``` 19 | 20 | 我们并不一定要在这使用`match`,例如,我们可以使用`if`: 21 | 22 | ```rust 23 | # let option = Some(5); 24 | # fn foo(x: i32) { } 25 | if option.is_some() { 26 | let x = option.unwrap(); 27 | foo(x); 28 | } 29 | ``` 30 | 31 | 这两种选项都不是特别吸引人。我们可以使用`if let`来优雅地完成相同的功能: 32 | 33 | ```rust 34 | # let option = Some(5); 35 | # fn foo(x: i32) { } 36 | if let Some(x) = option { 37 | foo(x); 38 | } 39 | ``` 40 | 41 | 如果一个[模式](Patterns 模式.md)匹配成功,它绑定任何值的合适的部分到模式的标识符中,并计算这个表达式。如果模式不匹配,啥也不会发生。 42 | 43 | 如果你想在模式不匹配时做点其他的,你可以使用`else`: 44 | 45 | ```rust 46 | # let option = Some(5); 47 | # fn foo(x: i32) { } 48 | # fn bar() { } 49 | if let Some(x) = option { 50 | foo(x); 51 | } else { 52 | bar(); 53 | } 54 | ``` 55 | 56 | ## `while let` 57 | 类似的,当你想一直循环,直到一个值匹配到特定的模式的时候,你可以选择使用`while let`。使用`while let`可以把类似这样的代码: 58 | 59 | ```rust 60 | let mut v = vec![1, 3, 5, 7, 11]; 61 | loop { 62 | match v.pop() { 63 | Some(x) => println!("{}", x), 64 | None => break, 65 | } 66 | } 67 | ``` 68 | 69 | 变成这样的代码: 70 | 71 | ```rust 72 | let mut v = vec![1, 3, 5, 7, 11]; 73 | while let Some(x) = v.pop() { 74 | println!("{}", x); 75 | } 76 | ``` 77 | -------------------------------------------------------------------------------- /content/`const` and `static`.md: -------------------------------------------------------------------------------- 1 | # const 和 static 2 | 3 | > [const-and-static.md](https://github.com/rust-lang/book/blob/master/first-edition/src/const-and-static.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | Rust 有一个用`const`关键字定义常量的方法: 8 | 9 | ```rust 10 | const N: i32 = 5; 11 | ``` 12 | 13 | 与[let](Variable Bindings 变量绑定.md)绑定不同,你必须标注一个`const`的类型。 14 | 15 | 常量贯穿于整个程序的生命周期。更具体的,Rust 中的常量并没有固定的内存地址。这是因为实际上它们会被内联到用到它们的地方。为此对同一常量的引用并不能保证引用到相同的内存地址。 16 | 17 | ## `static` 18 | 19 | Rust 以静态量的方式提供了类似“全局变量”的功能。它们与常量类似,不过静态量在使用时并不内联。这意味着对每一个值只有一个实例,并且位于内存中的固定位置。 20 | 21 | 这是一个例子: 22 | 23 | ```rust 24 | static N: i32 = 5; 25 | ``` 26 | 27 | 与[let](Variable Bindings 变量绑定.md)绑定不同,你必须标注一个`static`的类型。 28 | 29 | 静态量贯穿于整个程序的生命周期,因此任何存储在常量中的引用有一个[`'static`生命周期](Lifetimes 生命周期.md): 30 | 31 | ```rust 32 | static NAME: &'static str = "Steve"; 33 | ``` 34 | 35 | ## 可变性 36 | 37 | 你可以用`mut`关键字引入可变性: 38 | 39 | ```rust 40 | static mut N: i32 = 5; 41 | ``` 42 | 43 | 因为这是可变的,一个线程可能在更新`N`同时另一个在读取它,导致内存不安全。因此访问和改变一个`static mut`是[不安全(unsafe)](`unsafe` 不安全代码.md)的,因此必须在`unsafe`块中操作: 44 | 45 | ```rust 46 | # static mut N: i32 = 5; 47 | 48 | unsafe { 49 | N += 1; 50 | 51 | println!("N: {}", N); 52 | } 53 | ``` 54 | 55 | 更进一步,任何存储在`static`的类型必须实现`Sync`。 56 | 57 | ## 初始化 58 | `const`和`static`都要求赋予它们一个值。它们必须只能被赋予一个常量表达式的值。换句话说,你不能用一个函数调用的返回值或任何相似的复合值或在运行时赋值。 59 | 60 | ## 我应该用哪个?(Which construct should I use?) 61 | 62 | 几乎所有时候,如果你可以在两者之间选择,选择`const`。实际上你很少需要你的常量关联一个内存位置,而且使用`const`允许你不止在在自己的包装箱还可以在下游包装箱中使用像常数扩散这样的优化。 63 | 64 | 一个常量可以看作一个 C 中的`#define`:它有元数据开销但无运行时开销。“我应该在 C 中用一个 #define 还是一个 static 呢?”大体上与在 Rust 你应该用常量还是静态量是一个问题。 65 | -------------------------------------------------------------------------------- /content/Glossary 词汇表.md: -------------------------------------------------------------------------------- 1 | # 词汇表 2 | 3 | > [glossary.md](https://github.com/rust-lang/book/blob/master/first-edition/src/glossary.md) 4 | >
5 | > commit de288b41b5950c5075236c08c55b1b0ab2b35542 6 | 7 | 不是每位 Rustacean 都是系统编程或计算机科学背景的,所以我们加上了可能难以理解的词汇解释。 8 | 9 | ## 数量(Arity) 10 | 11 | Arity代表函数或操作所需的参数数量。 12 | ```rust 13 | let x = (2, 3); 14 | let y = (4, 6); 15 | let z = (8, 2, 6); 16 | ``` 17 | 在上面的例子中`x`和`y`的Arity是`2`,`z`的Arity是`3`。 18 | 19 | ## 抽象语法树(Abstract Syntax Tree) 20 | 21 | 当一个编译器编译你程序的时候,它做了很多不同的事。其中之一就是将你程序中的文本转换为一个‘抽象语法树’,或者‘AST’。这个树是你程序结构的表现。例如,`2 + 3`可以转换为一个树: 22 | 23 | ```text 24 | + 25 | / \ 26 | 2 3 27 | ``` 28 | 29 | 而`2 + (3 * 4)`看起来像这样: 30 | 31 | ```text 32 | + 33 | / \ 34 | 2 * 35 | / \ 36 | 3 4 37 | ``` 38 | 39 | ## 参数数量(Arity) 40 | 41 | Arity 代表一个函数或操作获取的参数的数量。 42 | 43 | ```rust 44 | let x = (2, 3); 45 | let y = (4, 6); 46 | let z = (8, 2, 6); 47 | ``` 48 | 49 | 在上面这个例子中`x`和`y`的arity是 2。`z`的arity是 3。 50 | 51 | ## 界限(Bounds) 52 | 53 | 界限是一个类型或[trait](Traits.md)的限制。例如,如果界限位于函数参数,那么传递给函数的参数类型必须遵守这个限制。 54 | 55 | ### 连接符(Combinators) 56 | 57 | 连接符是一种高级函数,它只作用于函数和其之前定义的连接符,通过其参数来提供一个结果。可以被用来以一种模块化的方式来控制流程。 58 | 59 | ## 动态大小类型(DST (Dynamically Sized Type)) 60 | 61 | 一个没有静态大小或对齐的类型。([更多信息](http://doc.rust-lang.org/nomicon/exotic-sizes.html#dynamically-sized-types-dsts)) 62 | 63 | ## 表达式(Expression) 64 | 65 | 在计算机编程中,一个表达式是一个值,常量,变量,运算符和函数的组合,它可以产生一个单一的值。例如,`2 + (3 * 4)`是一个返回14的表达式。值得注意的是表达式可以产生副作用。例如,一个表达式中的函数可能会执行一些不只是简单的返回一个值的操作。 66 | 67 | ## 面向表达式语言(Expression-Oriented Language) 68 | 69 | 在早期的编程语言中,[表达式](#表达式)和[语句](#语句)时两个不同句法范畴:表达式有一个值而语句做一件事。然而,之后的语言模糊了这个区别,允许表达式执行操作而让语句有一个值。在一个面向表达式的语言中,(几乎)所有语句都是一个表达式并因此返回一个值。由此,这些表达式自身也可以是更大表达式的一部分。 70 | 71 | ## 语句(Statement) 72 | 73 | 在计算机编程中,一个语句是一个编程语言能让计算执行操的最小的独立元素。 74 | -------------------------------------------------------------------------------- /content/Conditional Compilation 条件编译.md: -------------------------------------------------------------------------------- 1 | # 条件编译 2 | 3 | > [conditional-compilation.md](https://github.com/rust-lang/book/blob/master/first-edition/src/conditional-compilation.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | Rust 有一个特殊的属性,`#[cfg]`,它允许你基于一个传递给编译器的标记编译代码。它有两种形式: 8 | 9 | ```rust 10 | #[cfg(foo)] 11 | # fn foo() {} 12 | 13 | #[cfg(bar = "baz")] 14 | # fn bar() {} 15 | ``` 16 | 17 | 它还有一些帮助选项: 18 | 19 | ```rust 20 | #[cfg(any(unix, windows))] 21 | # fn foo() {} 22 | 23 | #[cfg(all(unix, target_pointer_width = "32"))] 24 | # fn bar() {} 25 | 26 | #[cfg(not(foo))] 27 | # fn not_foo() {} 28 | ``` 29 | 30 | 这些选项可以任意嵌套: 31 | 32 | ```rust 33 | #[cfg(any(not(unix), all(target_os="macos", target_arch = "powerpc")))] 34 | # fn foo() {} 35 | ``` 36 | 37 | 至于如何启用和禁用这些开关,如果你使用 Cargo 的话,它们可以在你`Cargo.toml`中的[`[features]`部分](http://doc.crates.io/manifest.html#the-%5Bfeatures%5D-section)设置: 38 | 39 | ```toml 40 | [features] 41 | # no features by default 42 | default = [] 43 | 44 | # Add feature "foo" here, then you can use it. 45 | # Our "foo" feature depends on nothing else. 46 | foo = [] 47 | ``` 48 | 49 | 当你这么做的时候,Cargo传递给`rustc`一个标记: 50 | 51 | ```bash 52 | --cfg feature="${feature_name}" 53 | ``` 54 | 55 | 这些`cfg`标记集合会决定哪些功能被启用,并且因此,哪些代码会被编译。让我们看看这些代码: 56 | 57 | ```rust 58 | #[cfg(feature = "foo")] 59 | mod foo { 60 | } 61 | ``` 62 | 63 | 如果你用`cargo build --features "foo"`编译,他会向`rustc`传递`--cfg feature="foo"`标记,并且输出中将会包含`mod foo`。如果我们使用常规的`cargo build`编译,则不会传递额外的标记,因此,(输出)不会存在`foo`模块。 64 | 65 | ## cfg_attr 66 | 67 | 你也可以通过一个基于`cfg`变量的`cfg_attr`来设置另一个属性: 68 | 69 | ```rust 70 | #[cfg_attr(a, b)] 71 | # fn foo() {} 72 | ``` 73 | 74 | 如果`a`通过`cfg`属性设置了的话这与`#[b]`相同,否则不起作用。 75 | 76 | # cfg! 77 | 78 | `cfg!`[语法扩展](Compiler Plugins 编译器插件.md)也让你可以在你的代码中使用这类标记: 79 | 80 | ```rust 81 | if cfg!(target_os = "macos") || cfg!(target_os = "ios") { 82 | println!("Think Different!"); 83 | } 84 | ``` 85 | 86 | 这会在编译时被替换为一个`true`或`false`,依配置设定而定。 87 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # 贡献者 2 | 3 | *惯例排名不分先后* 4 | 5 | * [aakloxu](https://github.com/aakloxu) 6 | * [angusdwhite](https://github.com/angusdwhite) 7 | * [armink](https://github.com/armink) 8 | * [ARwMq9b6](https://github.com/ARwMq9b6) 9 | * [BingCheung](https://github.com/BingCheung) 10 | * [Bluek404](https://github.com/Bluek404) 11 | * [davidwudv](https://github.com/davidwudv) 12 | * [hczhcz](https://github.com/hczhcz) 13 | * [hltj](https://github.com/hltj) 14 | * [honorabrutroll](https://github.com/honorabrutroll) 15 | * [hy0kl](https://github.com/hy0kl) 16 | * [imxiaozhi](https://github.com/imxiaozhi) 17 | * [iovxw](https://github.com/iovxw) 18 | * [JaySon-Huang](https://github.com/JaySon-Huang) 19 | * [KaiserY](https://github.com/KaiserY) 20 | * [KaleoCheng](https://github.com/KaleoCheng) 21 | * [kenshinji](https://github.com/kenshinji) 22 | * [kimw](https://github.com/kimw) 23 | * [leqinotes](https://github.com/leqinotes) 24 | * [linjx](https://github.com/linjx) 25 | * [liubin](https://github.com/liubin) 26 | * [liuchong](https://github.com/liuchong) 27 | * [liuzhe0223](https://github.com/liuzhe0223) 28 | * [llluiop](https://github.com/llluiop) 29 | * [loulihto](https://github.com/loulihto) 30 | * [LuoZijun](https://github.com/LuoZijun) 31 | * [mapx](https://github.com/mapx) 32 | * [NemoAlex](https://github.com/NemoAlex) 33 | * [newbienewbie](https://github.com/newbienewbie) 34 | * [opticaline](https://github.com/opticaline) 35 | * [peng1999](https://github.com/peng1999) 36 | * [qiuyesuifeng](https://github.com/qiuyesuifeng) 37 | * [quxiaolong1504](https://github.com/quxiaolong1504) 38 | * [rod-lin](https://github.com/rod-lin) 39 | * [tinunkai](https://github.com/tinunkai) 40 | * [t123yh](https://github.com/t123yh) 41 | * [ustcscgy](https://github.com/ustcscgy) 42 | * [vra](https://github.com/vra) 43 | * [weaming](https://github.com/weaming) 44 | * [wzv5](https://github.com/wzv5) 45 | * [yqylovy](https://github.com/yqylovy) 46 | * [zhukunqian](https://github.com/zhukunqian) 47 | * [ziqin](https://github.com/ziqin) 48 | * [zzh1996](https://github.com/zzh1996) 49 | * [1989car](https://github.com/1989car) 50 | -------------------------------------------------------------------------------- /content/Box Syntax and Patterns 装箱语法和模式.md: -------------------------------------------------------------------------------- 1 | # 装箱语法和模式 2 | 3 | > [box-syntax-and-patterns.md](https://github.com/rust-lang/rust/blob/stable/src/doc/book/box-syntax-and-patterns.md) 4 | >
5 | > commit 28548db57d0acbc00ee80b43816953dbe31d53ba 6 | 7 | 目前唯一稳定的创建`Box`的方法是通过`Box::new`方法。并且不可能在一个模式匹配中稳定的析构一个`Box`。不稳定的`box`关键字可以用来创建和析构`Box`。下面是一个用例: 8 | 9 | ```rust 10 | #![feature(box_syntax, box_patterns)] 11 | 12 | fn main() { 13 | let b = Some(box 5); 14 | match b { 15 | Some(box n) if n < 0 => { 16 | println!("Box contains negative number {}", n); 17 | }, 18 | Some(box n) if n >= 0 => { 19 | println!("Box contains non-negative number {}", n); 20 | }, 21 | None => { 22 | println!("No box"); 23 | }, 24 | _ => unreachable!() 25 | } 26 | } 27 | ``` 28 | 29 | 注意这些功能目前隐藏在`box_syntax`(装箱创建)和`box_patterns`(析构和模式匹配)gate 之后因为它的语法在未来可能会改变。 30 | 31 | ## 返回指针 32 | 33 | 在很多有指针的语言中,你的函数可以返回一个指针来避免拷贝大的数据结构。例如: 34 | 35 | ```rust 36 | struct BigStruct { 37 | one: i32, 38 | two: i32, 39 | // Etc. 40 | one_hundred: i32, 41 | } 42 | 43 | fn foo(x: Box) -> Box { 44 | Box::new(*x) 45 | } 46 | 47 | fn main() { 48 | let x = Box::new(BigStruct { 49 | one: 1, 50 | two: 2, 51 | one_hundred: 100, 52 | }); 53 | 54 | let y = foo(x); 55 | } 56 | ``` 57 | 58 | 要点是通过传递一个装箱,你只需拷贝了一个指针,而不是那构成了`BigStruct`的一百个`int`值。 59 | 60 | 上面是 Rust 中的一个反模式。相反,这样写: 61 | 62 | ```rust 63 | #![feature(box_syntax)] 64 | 65 | struct BigStruct { 66 | one: i32, 67 | two: i32, 68 | // Etc. 69 | one_hundred: i32, 70 | } 71 | 72 | fn foo(x: Box) -> BigStruct { 73 | *x 74 | } 75 | 76 | fn main() { 77 | let x = Box::new(BigStruct { 78 | one: 1, 79 | two: 2, 80 | one_hundred: 100, 81 | }); 82 | 83 | let y: Box = box foo(x); 84 | } 85 | ``` 86 | 87 | 这在不牺牲性能的前提下获得了灵活性。 88 | 89 | 你可能会认为这会给我们带来很差的性能:返回一个值然后马上把它装箱?难道这在哪里不都是最糟的吗?Rust 显得更聪明。这里并没有拷贝。`main`为装箱分配了足够的空间,向`foo`传递一个指向他内存的`x`,然后`foo`直接向`Box`中写入数据。 90 | 91 | 因为这很重要所以要说两遍:返回指针会阻止编译器优化你的代码。允许调用函数选择它们需要如何使用你的输出。 92 | -------------------------------------------------------------------------------- /content/Borrow and AsRef Borrow 和 AsRef.md: -------------------------------------------------------------------------------- 1 | # `Borrow` 和 `AsRef` 2 | 3 | > [borrow-and-asref.md](https://github.com/rust-lang/book/blob/master/first-edition/src/borrow-and-asref.md) 4 | >
5 | > commit 23a7a7bdb6a6a43cd7efdd9176b1d3f75d9d0e70 6 | 7 | [`Borrow`](http://doc.rust-lang.org/std/borrow/trait.Borrow.html)和[`AsRef`](http://doc.rust-lang.org/std/convert/trait.AsRef.html)特性非常相似。这是一个快速的关于这两个特性意义的复习。 8 | 9 | ## `Borrow` 10 | 11 | `Borrow`特性用于当你处于某种目的写了一个数据结构,并且你想要使用一个要么拥有要么借用的类型作为它的同义词。 12 | 13 | 例如,[`HashMap`](http://doc.rust-lang.org/std/collections/struct.HashMap.html)有一个用了`Borrow`的[`get`方法](http://doc.rust-lang.org/std/collections/struct.HashMap.html#method.get): 14 | 15 | ```rust 16 | fn get(&self, k: &Q) -> Option<&V> 17 | where K: Borrow, 18 | Q: Hash + Eq 19 | ``` 20 | 21 | 这个签名非常复杂。`k`参数是我们感兴趣的。它引用了一个`HashMap`自身的参数: 22 | 23 | ```rust 24 | struct HashMap { 25 | ``` 26 | 27 | `k`参数是`HashMap`用的`key`类型。所以,再一次查看`get()`的签名,我们可以在键实现了`Borrow`时使用`get()`。这样,我们可以创建一个`HashMap`,它使用`String`键,不过在我们搜索时使用`&str`: 28 | 29 | ```rust 30 | use std::collections::HashMap; 31 | 32 | let mut map = HashMap::new(); 33 | map.insert("Foo".to_string(), 42); 34 | 35 | assert_eq!(map.get("Foo"), Some(&42)); 36 | ``` 37 | 38 | 这是因为标准库中有`impl Borrow for String`(为 String 实现了Borrow)。 39 | 40 | 对于多数类型,当你想要获取一个自我拥有或借用的类型,`&T`就足够了。不过当有多于一种借用的值时,`Borrow`就能起作用了。引用和`slice`就是一个能体现这一点的地方:你可以有`&[T]`或者`&mut [T]`。如果我们想接受这两种类型,`Borrow`就是你需要的: 41 | 42 | ```rust 43 | use std::borrow::Borrow; 44 | use std::fmt::Display; 45 | 46 | fn foo + Display>(a: T) { 47 | println!("a is borrowed: {}", a); 48 | } 49 | 50 | let mut i = 5; 51 | 52 | foo(&i); 53 | foo(&mut i); 54 | ``` 55 | 56 | 这会打印出`a is borrowed: 5`两次。 57 | 58 | ## `AsRef` 59 | 60 | `AsRef`特性是一个转换特性。它用来在泛型中把一些值转换为引用。像这样: 61 | 62 | ```rust 63 | let s = "Hello".to_string(); 64 | 65 | fn foo>(s: T) { 66 | let slice = s.as_ref(); 67 | } 68 | ``` 69 | 70 | ## 我应该用哪个? 71 | 72 | 我们可以看到它们有些相似:它们都处理一些类型的自我拥有和借用版本。然而,它们还是有些不同。 73 | 74 | 选择`Borrow`当你想要抽象不同类型的借用,或者当你创建一个数据结构它把自我拥有和借用的值看作等同的,例如哈希和比较。 75 | 76 | 选择`AsRef`当你想要直接把一些值转换为引用,和当你在写泛型代码的时候。 77 | -------------------------------------------------------------------------------- /content/Enums 枚举.md: -------------------------------------------------------------------------------- 1 | # 枚举 2 | 3 | > [enums.md](https://github.com/rust-lang/book/blob/master/first-edition/src/enums.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | Rust 中的一个`enum`是一个代表数个可能变量的数据的类型。`enum`中的每个变量都可选是否关联数据: 8 | 9 | ```rust 10 | enum Message { 11 | Quit, 12 | ChangeColor(i32, i32, i32), 13 | Move { x: i32, y: i32 }, 14 | Write(String), 15 | } 16 | ``` 17 | 18 | 定义变量的语法与用来定义结构体的语法类似:你可以有不带数据的变量(像类单元结构体),带有命名数据的变量,和带有未命名数据的变量(像元组结构体)。然而,不像单独的结构体定义,一个`enum`是一个单独的类型。一个枚举的值可以匹配任何一个变量。因为这个原因,枚举有时被叫做“集合类型”:枚举可能值的集合是每一个变量可能值的集合的总和。 19 | 20 | 我们使用`::`语法来使用每个变量的名字:它们包含在`enum`名字自身中。这样的话,以下的情况都是可行的: 21 | 22 | ```rust 23 | # enum Message { 24 | # Move { x: i32, y: i32 }, 25 | # } 26 | let x: Message = Message::Move { x: 3, y: 4 }; 27 | 28 | enum BoardGameTurn { 29 | Move { squares: i32 }, 30 | Pass, 31 | } 32 | 33 | let y: BoardGameTurn = BoardGameTurn::Move { squares: 1 }; 34 | ``` 35 | 36 | 这两个变量都叫做`Move`,不过他们包含在枚举名字中,他们可以无冲突的使用。 37 | 38 | 枚举类型的一个值包含它是哪个变量的信息,以及任何与变量相关的数据。这有时被作为一个“标记的联合”被提及。因为数据包括一个“标签”表明它的类型是什么。编译器使用这个信息来确保安全的访问枚举中的数据。例如,我们不能简单的尝试解构一个枚举值,就像它是其中一个可能的变体那样: 39 | 40 | ```rust,ignore 41 | fn process_color_change(msg: Message) { 42 | let Message::ChangeColor(r, g, b) = msg; // This causes a compile-time error. 43 | } 44 | ``` 45 | 46 | 不支持这些操作(比较操作)可能看起来更像限制。不过这是一个我们可以克服的限制。有两种方法:我们自己实现相等(比较),或通过[`match` ](Match 匹配.md)表达式模式匹配变量,你会在下一部分学到它。我们还不够了解Rust如何实现相等,不过我们会在[特性](Traits.md)找到它们。 47 | 48 | ## 构造器作为函数(Constructors as functions) 49 | 50 | 一个枚举的构造器总是可以像函数一样使用。例如: 51 | 52 | ```rust 53 | # enum Message { 54 | # Write(String), 55 | # } 56 | let m = Message::Write("Hello, world".to_string()); 57 | ``` 58 | 59 | 与下面是一样的: 60 | 61 | ```rust 62 | # enum Message { 63 | # Write(String), 64 | # } 65 | fn foo(x: String) -> Message { 66 | Message::Write(x) 67 | } 68 | 69 | let x = foo("Hello, world".to_string()); 70 | ``` 71 | 72 | 这对我们没有什么直接的帮助,直到我们要用到[闭包](Closures 闭包.md)时,这时我们要考虑将函数作为参数传递给其他函数。例如,使用[迭代器](Iterators 迭代器.md),我们可以这样把一个`String`的vector转换为一个`Message::Write`的vector: 73 | 74 | ```rust 75 | # enum Message { 76 | # Write(String), 77 | # } 78 | 79 | let v = vec!["Hello".to_string(), "World".to_string()]; 80 | 81 | let v1: Vec = v.into_iter().map(Message::Write).collect(); 82 | ``` 83 | -------------------------------------------------------------------------------- /content/Match 匹配.md: -------------------------------------------------------------------------------- 1 | # 匹配 2 | 3 | > [match.md](https://github.com/rust-lang/book/blob/master/first-edition/src/match.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | 一个简单的[`if`](If If语句.md)/`else`往往是不够的,因为你可能有两个或更多个选项。这样`else`也会变得异常复杂。Rust 有一个`match`关键字,它可以让你有效的取代复杂的`if`/`else`组。看看下面的代码: 8 | 9 | ```rust 10 | let x = 5; 11 | 12 | match x { 13 | 1 => println!("one"), 14 | 2 => println!("two"), 15 | 3 => println!("three"), 16 | 4 => println!("four"), 17 | 5 => println!("five"), 18 | _ => println!("something else"), 19 | } 20 | ``` 21 | 22 | `match`使用一个表达式然后基于它的值分支。每个分支都是`val => expression`这种形式。当匹配到一个分支,它的表达式将被执行。`match`属于“模式匹配”的范畴,`match`是它的一个实现。有[一个整个关于模式的部分](Patterns 模式.md)讲到了所有可能的模式。 23 | 24 | 那么这有什么巨大的优势呢?这确实有优势。第一,`match`强制**穷尽性检查**(*exhaustiveness checking*)。你看到了最后那个下划线开头的分支了吗?如果去掉它,Rust 将会给我们一个错误: 25 | 26 | ```text 27 | error: non-exhaustive patterns: `_` not covered 28 | ``` 29 | 30 | Rust 试图告诉我们忘记了一个值。编译器从`x`推断它可以是任何 32 位整型值;例如从 -2,147,483,648 到 2,147,483,647。`_`就像一个**匹配所有**的分支,它会捕获所有没有被`match`分支捕获的所有可能值。如你所见,在上个例子中,我们提供了 1 到 5 的`mtach`分支,如果`x`是 6 或者其他值,那么它会被`_`捕获。 31 | 32 | `match`也是一个表达式,也就是说它可以用在`let`绑定的右侧或者其它直接用到表达式的地方: 33 | 34 | ```rust 35 | let x = 5; 36 | 37 | let number = match x { 38 | 1 => "one", 39 | 2 => "two", 40 | 3 => "three", 41 | 4 => "four", 42 | 5 => "five", 43 | _ => "something else", 44 | }; 45 | ``` 46 | 47 | 有时,这是一个把一种类型的数据转换为另一个类型的好方法。 48 | 49 | ## 匹配枚举(Matching on enums) 50 | 51 | `match`的另一个重要的作用是处理枚举的可能变量: 52 | 53 | ```rust 54 | enum Message { 55 | Quit, 56 | ChangeColor(i32, i32, i32), 57 | Move { x: i32, y: i32 }, 58 | Write(String), 59 | } 60 | 61 | fn quit() { /* ... */ } 62 | fn change_color(r: i32, g: i32, b: i32) { /* ... */ } 63 | fn move_cursor(x: i32, y: i32) { /* ... */ } 64 | 65 | fn process_message(msg: Message) { 66 | match msg { 67 | Message::Quit => quit(), 68 | Message::ChangeColor(r, g, b) => change_color(r, g, b), 69 | Message::Move { x, y: new_name_for_y } => move_cursor(x, new_name_for_y), 70 | Message::Write(s) => println!("{}", s), 71 | }; 72 | } 73 | ``` 74 | 75 | 再一次,Rust编译器检查穷尽性,所以它要求对每一个枚举的变量都有一个匹配分支。如果你忽略了一个,除非你用`_`否则它会给你一个编译时错误。 76 | 77 | 与之前的`match`的作用不同,你不能用常规的`if`语句来做这些。你可以使用[if let](if let.md)语句,它可以被看作是一个`match`的简略形式。 78 | -------------------------------------------------------------------------------- /content/Lang items 语言项.md: -------------------------------------------------------------------------------- 1 | # 语言项(Lang items) 2 | 3 | > [lang-items.md](https://github.com/rust-lang/rust/blob/stable/src/doc/book/lang-items.md) 4 | >
5 | > commit 893f42a83466cf02b6fd6d3c82d5419cdad47474 6 | 7 | > **注意**:语言项通常由 Rust 发行版的 crate 提供,并且它自身有一个不稳定的接口。建议使用官方发布的 crate 而不是定义自己的版本。 8 | 9 | `rustc`编译器有一些可插入的操作,也就是说,功能不是硬编码进语言的,而是在库中实现的,通过一个特殊的标记告诉编译器它存在。这个标记是`#[lang="..."]`属性并且有不同的值`...`,也就是不同的“语言项”。 10 | 11 | 例如,`Box`指针需要两个语言项,一个用于分配,一个用于释放。下面是一个独立的程序使用`Box`语法糖进行动态分配,通过`malloc`和`free`: 12 | 13 | ```rust,ignore 14 | #![feature(lang_items, box_syntax, start, libc, core_intrinsics)] 15 | #![no_std] 16 | use core::intrinsics; 17 | 18 | extern crate libc; 19 | 20 | #[lang = "owned_box"] 21 | pub struct Box(*mut T); 22 | 23 | #[lang = "exchange_malloc"] 24 | unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { 25 | let p = libc::malloc(size as libc::size_t) as *mut u8; 26 | 27 | // Check if `malloc` failed: 28 | if p as usize == 0 { 29 | intrinsics::abort(); 30 | } 31 | 32 | p 33 | } 34 | 35 | #[lang = "exchange_free"] 36 | unsafe fn deallocate(ptr: *mut u8, _size: usize, _align: usize) { 37 | libc::free(ptr as *mut libc::c_void) 38 | } 39 | 40 | #[lang = "box_free"] 41 | unsafe fn box_free(ptr: *mut T) { 42 | deallocate(ptr as *mut u8, ::core::mem::size_of_val(&*ptr), ::core::mem::align_of_val(&*ptr)); 43 | } 44 | 45 | #[start] 46 | fn main(argc: isize, argv: *const *const u8) -> isize { 47 | let x = box 1; 48 | 49 | 0 50 | } 51 | 52 | #[lang = "eh_personality"] extern fn rust_eh_personality() {} 53 | #[lang = "panic_fmt"] extern fn rust_begin_panic() -> ! { unsafe { intrinsics::abort() } 54 | # #[lang = "eh_unwind_resume"] extern fn rust_eh_unwind_resume() {} 55 | # #[no_mangle] pub extern fn rust_eh_register_frames () {} 56 | # #[no_mangle] pub extern fn rust_eh_unregister_frames () {} 57 | ``` 58 | 59 | 注意`abort`的使用:`exchange_malloc`语言项假设返回一个有效的指针,所以需要在内部进行检查。 60 | 61 | 其它语言项提供的功能包括: 62 | 63 | * 通过特性重载运算符:`==`,`<`,解引用(`*`)和`+`等运算符对应的特性都有语言项标记;上面4个分别为`eq`,`ord`,`deref`和`add` 64 | * 栈展开和一般故障:`eh_personality`,`fail`和`fail_bounds_checks`语言项 65 | * `std::marker`中用来标明不同类型的特性:`send`,`sync`和`copy`。 66 | * `std::marker`中的标记类型和变化指示器:`covariant_type`和`contravariant_lifetime`等 67 | 68 | 语言项由编译器延时加载;例如,如果你从未用过`Box`则就没有必要定义`exchange_malloc`和`exchange_free`的函数。`rustc`在一个项被需要而无法在当前包装箱或任何依赖中找到时生成一个错误。 69 | -------------------------------------------------------------------------------- /content/Nightly Rust Rust 开发版.md: -------------------------------------------------------------------------------- 1 | # Rust开发版 2 | 3 | > [nightly-rust.md](https://github.com/rust-lang/rust/blob/stable/src/doc/book/nightly-rust.md) 4 | >
5 | > commit eb1c7161dd79b55e022cd0c661f9018d406b3fe4 6 | 7 | Rust 提供了三种发行渠道:开发版(每日构建),beta 版和稳定版。不稳定功能只在 Rust 开发版中可用。对于这个进程的更多细节,参见[可交付产品的稳定性](http://blog.rust-lang.org/2014/10/30/Stability.html)。 8 | 9 | 要安装 Rust 开发版,你可以使用`rustup.sh`: 10 | 11 | ```bash 12 | $ curl -s https://static.rust-lang.org/rustup.sh | sh -s -- --channel=nightly 13 | ``` 14 | 15 | 如果你担心使用`curl | sh`的[潜在不安全性](http://curlpipesh.tumblr.com),请继续阅读并查看我们下面的免责声明。你也可以进行两步安装,以便于检查我们的安装脚本: 16 | 17 | ```bash 18 | $ curl -f -L https://static.rust-lang.org/rustup.sh -O 19 | $ sh rustup.sh --channel=nightly 20 | ``` 21 | 22 | 如果你用 Windows,请直接下载[32位安装包][win32]或者[64位安装包][win64]然后运行即可。 23 | 24 | [win32]: https://static.rust-lang.org/dist/rust-nightly-i686-pc-windows-gnu.msi 25 | [win64]: https://static.rust-lang.org/dist/rust-nightly-x86_64-pc-windows-gnu.msi 26 | 27 | ## 卸载 28 | 29 | 如果你决定不再需要 Rust 了,我们会有点难过,但没关系。不是每种编程语言都适合所有人。运行下面的卸载脚本即可: 30 | 31 | ```bash 32 | $ sudo /usr/local/lib/rustlib/uninstall.sh 33 | ``` 34 | 35 | 如果你使用 Windows 安装包进行安装的话,重新运行`.msi`文件,它会提供卸载选项。 36 | 37 | 一些同学确实有理由对我们让他们运行`curl | sudo sh`感到反感。从根本上说,当你运行上面的脚本时,代表你相信是一些好人在维护 Rust,他们不会黑了你的电脑做坏事。对此保持警觉是很好的天性。如果你是这些人之一,请检阅以下文档:[从源码编译Rust](https://github.com/rust-lang/rust#building-from-source)或者[官方二进制文件下载](https://www.rust-lang.org/install.html)。 38 | 39 | 当然,我们还应该提到官方支持的平台: 40 | 41 | * Windows(7+) 42 | * Linux(2.6.18 或更高版本,各种发行版),x86 和 x86-64 43 | * OSX 10.7(Lion)或更高版本,x86 和 x86-64 44 | 45 | Rust 在以上平台进行了广泛的测试,当然其他一些平台也有,比如 Android。不过在上述平台工作起来最顺畅,因为它们进行了最多的测试。 46 | 47 | 最后说说 Windows。Rust 将 Windows 作为第一级平台来发布,不过说实话,WIndows 的集成体验并没有 Linux/OS X 那么好。我们正在改进!如果有不能工作的情况,就是出了 bug。一旦发生了请告知我们。任何一次提交都会在 Windows 下进行测试,和其他平台无异。 48 | 49 | 如果你已安装 Rust,你可以打开一个 Shell,然后输入: 50 | 51 | ```bash 52 | $ rustc --version 53 | ``` 54 | 55 | 你应该看到版本号,提交的 hash 值,提交时间和构建时间: 56 | 57 | ```bash 58 | rustc 1.0.0-nightly (f11f3e7ba 2015-01-04) (built 2015-01-06) 59 | ``` 60 | 61 | 如果你做到了,那么 Rust 已经成功安装!此处应有掌声! 62 | 63 | 如果你遇到什么错误,有几个你可以获取帮助的地方。最简单的是通过 [Mibbit](http://chat.mibbit.com/?server=irc.mozilla.org&channel=%23rust) 访问 [Rust IRC 频道 irc.mozilla.org](irc://irc.mozilla.org/#rust)。点击上面的链接,你就可以与其他 Rustacean(我们这些人自称的绰号)聊天了,我们会帮助你。其他给力的资源包括[用户论坛](https://users.rust-lang.org/)和 [Stack Overflow](http://stackoverflow.com/questions/tagged/rust)。 64 | -------------------------------------------------------------------------------- /content/Raw Pointers 裸指针.md: -------------------------------------------------------------------------------- 1 | # 裸指针 2 | 3 | > [raw-pointers.md](https://github.com/rust-lang/book/blob/master/first-edition/src/raw-pointers.md) 4 | >
5 | > commit 23a7a7bdb6a6a43cd7efdd9176b1d3f75d9d0e70 6 | 7 | Rust 的标准库中有一系列不同的智能指针类型,不过这有两个类型是十分特殊的。Rust 的安全大多来源于编译时检查,不过裸指针并没有这样的保证,使用它们是[`unsafe`](`unsafe` 不安全代码.md)的。 8 | 9 | `*const T`和`*mut T`在 Rust 中被称为“裸指针”。有时当编写特定类型的库时,为了某些原因你需要绕过 Rust 的安全保障。在这种情况下,你可以使用裸指针来实现你的库,同时暴露一个安全的接口给你的用户。例如,`*`指针允许别名,允许用来写共享所有权类型,甚至是内存安全的共享内存类型(`Rc`和`Arc`类型都是完全用 Rust 实现的)。 10 | 11 | 有一些你需要记住的裸指针不同于其它指针的地方。它们是: 12 | 13 | * 不能保证指向有效的内存,甚至不能保证是非空的(不像`Box`和`&`); 14 | * 没有任何自动清除,不像`Box`,所以需要手动管理资源; 15 | * 是普通旧式类型,也就是说,它不移动所有权,这又不像`Box`,因此Rust编译器不能保证不出像释放后使用这种bug; 16 | * ~~被认为是可发送的(如果它的内容是可发送的),因此编译器不能提供帮助确保它的使用是线程安全的;例如,你可以从两个线程中并发的访问`*mut i32`而不用同步。~~ 17 | * 缺少任何形式的生命周期,不像`&`,因此编译器不能判断出悬垂指针; 18 | * 除了不允许直接通过`*const T`改变外,没有别名或可变性的保障。 19 | 20 | ## 基础 21 | 22 | 创建一个裸指针是非常安全的: 23 | 24 | ```rust 25 | let x = 5; 26 | let raw = &x as *const i32; 27 | 28 | let mut y = 10; 29 | let raw_mut = &mut y as *mut i32; 30 | ``` 31 | 32 | 然而,解引用它则不行。这个并不能工作: 33 | 34 | ```rust 35 | let x = 5; 36 | let raw = &x as *const i32; 37 | 38 | println!("raw points at {}", *raw); 39 | ``` 40 | 41 | 它给出这个错误: 42 | 43 | ```text 44 | error: dereference of raw pointer requires unsafe function or block [E0133] 45 | println!("raw points at {}", *raw); 46 | ^~~~ 47 | ``` 48 | 49 | 当你解引用一个裸指针,你要为它并不指向正确的地方负责。为此,你需要`unsafe`: 50 | 51 | ```rust 52 | let x = 5; 53 | let raw = &x as *const i32; 54 | 55 | let points_at = unsafe { *raw }; 56 | 57 | println!("raw points at {}", points_at); 58 | ``` 59 | 60 | 关于裸指针的更多操作,查看[它们的API文档](http://doc.rust-lang.org/stable/std/primitive.pointer.html)。 61 | 62 | ## FFI 63 | 64 | 裸指针在FFI中很有用:Rust的`*const T`和`*mut T`分别与C中的`const T*`和`T*`类似。关于它们的应用,查看[FFI章节](Foreign Function Interface 外部函数接口.md)。 65 | 66 | ## 引用和裸指针 67 | 68 | 在运行时,指向一份相同数据的裸指针`*`和引用有相同的表现。事实上,在安全代码中`&T`引用会隐式的转换为一个`*const T`同时它们的`mut`变体也有类似的行为(这两种转换都可以显式执行,分别为`value as *const T`和`value as *mut T`)。 69 | 70 | 反其道而行之,从`*const`到`&`引用,是不安全的。一个`&T`总是有效的,所以,最少,`*const T`裸指针必须指向一个`T`的有效实例。进一步,结果指针必须满足引用的别名和可变性法则。编译器假设这些属性对任何引用都是有效的,不管它们是如何创建的,因而所以任何从裸指针来的转换都断言它们成立。程序员**必须**保证它。 71 | 72 | 推荐的转换方法是 73 | 74 | ```rust 75 | // Explicit cast: 76 | let i: u32 = 1; 77 | let p_imm: *const u32 = &i as *const u32; 78 | 79 | // Implicit coercion: 80 | let mut m: u32 = 2; 81 | let p_mut: *mut u32 = &mut m; 82 | 83 | unsafe { 84 | let ref_imm: &u32 = &*p_imm; 85 | let ref_mut: &mut u32 = &mut *p_mut; 86 | } 87 | ``` 88 | 89 | 与使用`transmute`相比更倾向于`&*x`解引用风格。`transmute`远比需要的强大,并且(解引用)更受限的操作会更难以错误使用;例如,它要求`x`是一个指针(不像`transmute`)。 90 | -------------------------------------------------------------------------------- /content/`Deref` coercions `Deref`强制多态.md: -------------------------------------------------------------------------------- 1 | # `Deref`强制多态 2 | 3 | > [deref-coercions.md](https://github.com/rust-lang/book/blob/master/first-edition/src/deref-coercions.md) 4 | >
5 | > commit bd8d27beb54ef2a7bb4162d43006792f9ceae361 6 | 7 | 标准库提供了一个特殊的特性,[`Deref`](http://doc.rust-lang.org/stable/std/ops/trait.Deref.html)。它一般用来重载`*`,解引用运算符: 8 | 9 | ```rust 10 | use std::ops::Deref; 11 | 12 | struct DerefExample { 13 | value: T, 14 | } 15 | 16 | impl Deref for DerefExample { 17 | type Target = T; 18 | 19 | fn deref(&self) -> &T { 20 | &self.value 21 | } 22 | } 23 | 24 | fn main() { 25 | let x = DerefExample { value: 'a' }; 26 | assert_eq!('a', *x); 27 | } 28 | ``` 29 | 30 | 这对编写自定义指针类型很有用。然而,有一个与`Deref`相关的语言功能:“解引用强制多态(deref coercions)”。规则如下:如果你有一个`U`类型,和它的实现`Deref`,(那么)`&U`的值将会自动转换为`&T`。这是一个例子: 31 | 32 | ```rust 33 | fn foo(s: &str) { 34 | // Borrow a string for a second. 35 | } 36 | 37 | // String implements Deref. 38 | let owned = "Hello".to_string(); 39 | 40 | // Therefore, this works: 41 | foo(&owned); 42 | ``` 43 | 44 | 在一个值的前面用`&`号获取它的引用。所以`owned`是一个`String`,`&owned`是一个`&String`,而因为`impl Deref for String`,`&String`将会转换为`&str`,而它是`foo()`需要的。 45 | 46 | 这就是了。这是Rust唯一一个为你进行一个自动转换的地方,不过它增加了很多灵活性。例如,`Rc`类型实现了`Deref`,所以这可以工作: 47 | 48 | ```rust 49 | use std::rc::Rc; 50 | 51 | fn foo(s: &str) { 52 | // Borrow a string for a second. 53 | } 54 | 55 | // String implements Deref. 56 | let owned = "Hello".to_string(); 57 | let counted = Rc::new(owned); 58 | 59 | // Therefore, this works: 60 | foo(&counted); 61 | ``` 62 | 63 | 我们所做的一切就是把我们的`String`封装到了一个`Rc`里。不过现在我们可以传递`Rc`给任何我们有一个`String`的地方。`foo`的签名并无变化,不过它对这两个类型都能正常工作。这个例子有两个转换:`&Rc`转换为`&String`接着是`&String`转换为`&str`。只要类型匹配 Rust 将可以做任意多次这样的转换。 64 | 65 | 标准库提供的另一个非常通用的实现是: 66 | 67 | ```rust 68 | fn foo(s: &[i32]) { 69 | // Borrow a slice for a second. 70 | } 71 | 72 | // Vec implements Deref. 73 | let owned = vec![1, 2, 3]; 74 | 75 | foo(&owned); 76 | ``` 77 | 78 | 向量可以`Deref`为一个切片。 79 | 80 | ## `Deref`和方法调用 81 | 82 | 当调用一个方法时`Deref`也会出现。考虑下面的例子: 83 | 84 | ```rust 85 | struct Foo; 86 | 87 | impl Foo { 88 | fn foo(&self) { println!("Foo"); } 89 | } 90 | 91 | let f = &&Foo; 92 | 93 | f.foo(); 94 | ``` 95 | 96 | 即便`f`是`&&Foo`,而`foo`接受`&self`,这也是可以工作的。因为这些都是一样的: 97 | 98 | ```rust 99 | f.foo(); 100 | (&f).foo(); 101 | (&&f).foo(); 102 | (&&&&&&&&f).foo(); 103 | ``` 104 | 105 | 一个`&&&&&&&&&&&&&&&&Foo`类型的值仍然可以调用`Foo`定义的方法,因为编译器会插入足够多的`*`来使类型正确。而正因为它插入`*`,它用了`Deref`。 106 | -------------------------------------------------------------------------------- /content/Universal Function Call Syntax 通用函数调用语法.md: -------------------------------------------------------------------------------- 1 | # 通用函数调用语法 2 | 3 | > [ufcs.md](https://github.com/rust-lang/book/blob/master/first-edition/src/ufcs.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | 有时,函数可能有相同的名字。就像下面这些代码: 8 | 9 | ```rust 10 | trait Foo { 11 | fn f(&self); 12 | } 13 | 14 | trait Bar { 15 | fn f(&self); 16 | } 17 | 18 | struct Baz; 19 | 20 | impl Foo for Baz { 21 | fn f(&self) { println!("Baz’s impl of Foo"); } 22 | } 23 | 24 | impl Bar for Baz { 25 | fn f(&self) { println!("Baz’s impl of Bar"); } 26 | } 27 | 28 | let b = Baz; 29 | ``` 30 | 31 | 如果我们尝试调用`b.f()`,我们会得到一个错误: 32 | 33 | ```text 34 | error: multiple applicable methods in scope [E0034] 35 | b.f(); 36 | ^~~ 37 | note: candidate #1 is defined in an impl of the trait `main::Foo` for the type 38 | `main::Baz` 39 | fn f(&self) { println!("Baz’s impl of Foo"); } 40 | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 41 | note: candidate #2 is defined in an impl of the trait `main::Bar` for the type 42 | `main::Baz` 43 | fn f(&self) { println!("Baz’s impl of Bar"); } 44 | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 45 | ``` 46 | 47 | 我们需要一个区分我们需要调用哪一函数的方法。这个功能叫做“通用函数调用语法”(universal function call syntax),这看起来像这样: 48 | 49 | ```rust 50 | # trait Foo { 51 | # fn f(&self); 52 | # } 53 | # trait Bar { 54 | # fn f(&self); 55 | # } 56 | # struct Baz; 57 | # impl Foo for Baz { 58 | # fn f(&self) { println!("Baz’s impl of Foo"); } 59 | # } 60 | # impl Bar for Baz { 61 | # fn f(&self) { println!("Baz’s impl of Bar"); } 62 | # } 63 | # let b = Baz; 64 | Foo::f(&b); 65 | Bar::f(&b); 66 | ``` 67 | 68 | 让我们拆开来看。 69 | 70 | ```rust 71 | Foo:: 72 | Bar:: 73 | ``` 74 | 75 | 调用的这一半是两个 trait 的类型:`Foo`和`Bar`。这样实际上就区分了这两者:Rust 调用你使用的 trait 里面的方法。 76 | 77 | ```rust 78 | f(&b) 79 | ``` 80 | 81 | 当我们使用[方法语法](Method Syntax 方法语法.md)调用像`b.f()`这样的方法时,如果`f()`需要`&self`,Rust 实际上会自动地把`b`借用为`&self`。而在这个例子中,Rust 并不会这么做,所以我们需要显式地传递一个`&b`。 82 | 83 | ## 尖括号形式(Angle-bracket Form) 84 | 85 | 我们刚才讨论的通用函数调用语法的形式: 86 | 87 | ```rust 88 | Trait::method(args); 89 | ``` 90 | 91 | 上面的形式其实是一种缩写。这是在一些情况下需要使用的扩展形式: 92 | 93 | ```rust 94 | ::method(args); 95 | ``` 96 | 97 | `<>::`语法是一个提供类型提示的方法。类型位于`<>`中。在这个例子中,类型是`Type as Trait`,表示我们想要`method`的`Trait`版本被调用。在没有二义时`as Trait`部分是可选的。尖括号也是一样。因此上面的形式就是一种缩写的形式。 98 | 99 | 这是一个使用较长形式的例子。 100 | 101 | ```rust 102 | trait Foo { 103 | fn foo() -> i32; 104 | } 105 | 106 | struct Bar; 107 | 108 | impl Bar { 109 | fn foo() -> i32 { 110 | 20 111 | } 112 | } 113 | 114 | impl Foo for Bar { 115 | fn foo() -> i32 { 116 | 10 117 | } 118 | } 119 | 120 | fn main() { 121 | assert_eq!(10, ::foo()); 122 | assert_eq!(20, Bar::foo()); 123 | } 124 | ``` 125 | 126 | 使用尖括号语法让你可以调用指定 trait 的方法而不是继承到的那个。 127 | -------------------------------------------------------------------------------- /content/Operators and Overloading 运算符和重载.md: -------------------------------------------------------------------------------- 1 | # 运算符与重载 2 | 3 | > [operators-and-overloading.md](https://github.com/rust-lang/book/blob/master/first-edition/src/operators-and-overloading.md) 4 | >
5 | > commit 23a7a7bdb6a6a43cd7efdd9176b1d3f75d9d0e70 6 | 7 | Rust 允许有限形式的运算符重载。特定的运算符可以被重载。要支持一个类型间特定的运算符,你可以实现一个的特定的重载运算符的trait。 8 | 9 | 例如,`+`运算符可以通过`Add`特性重载: 10 | 11 | ```rust 12 | use std::ops::Add; 13 | 14 | #[derive(Debug)] 15 | struct Point { 16 | x: i32, 17 | y: i32, 18 | } 19 | 20 | impl Add for Point { 21 | type Output = Point; 22 | 23 | fn add(self, other: Point) -> Point { 24 | Point { x: self.x + other.x, y: self.y + other.y } 25 | } 26 | } 27 | 28 | fn main() { 29 | let p1 = Point { x: 1, y: 0 }; 30 | let p2 = Point { x: 2, y: 3 }; 31 | 32 | let p3 = p1 + p2; 33 | 34 | println!("{:?}", p3); 35 | } 36 | ``` 37 | 38 | 在`main`中,我们可以对我们的两个`Point`用`+`号,因为我们已经为`Point`实现了`Add`。 39 | 40 | 有一系列可以这样被重载的运算符,并且所有与之相关的trait都在[`std::ops`](http://doc.rust-lang.org/stable/std/ops/)模块中。查看它的文档来获取完整的列表。 41 | 42 | 实现这些特性要遵循一个模式。让我们仔细看看[`Add`](http://doc.rust-lang.org/stable/std/ops/trait.Add.html): 43 | 44 | ```rust 45 | # mod foo { 46 | pub trait Add { 47 | type Output; 48 | 49 | fn add(self, rhs: RHS) -> Self::Output; 50 | } 51 | # } 52 | ``` 53 | 54 | 这里总共涉及到 3 个类型:你`impl Add`的类型,`RHS`,它默认是`Self`,和`Output`。对于一个表达式`let z = x + y`,`x`是`Self`类型的,`y`是`RHS`,而`z`是`Self::Output`类型。 55 | 56 | ```rust 57 | # struct Point; 58 | # use std::ops::Add; 59 | impl Add for Point { 60 | type Output = f64; 61 | 62 | fn add(self, rhs: i32) -> f64 { 63 | // Add an i32 to a Point and get an f64. 64 | # 1.0 65 | } 66 | } 67 | ``` 68 | 69 | 将允许你这样做: 70 | 71 | ```rust 72 | let p: Point = // ... 73 | let x: f64 = p + 2i32; 74 | ``` 75 | 76 | ## 在泛型结构体中使用运算符 trait 77 | 78 | 现在我们知道了运算符 trait 是如何定义的了,我们可以更通用的定义来自[trait 章节]()的`HasArea` trait 和`Square`结构体: 79 | 80 | ```rust 81 | use std::ops::Mul; 82 | 83 | trait HasArea { 84 | fn area(&self) -> T; 85 | } 86 | 87 | struct Square { 88 | x: T, 89 | y: T, 90 | side: T, 91 | } 92 | 93 | impl HasArea for Square 94 | where T: Mul + Copy { 95 | fn area(&self) -> T { 96 | self.side * self.side 97 | } 98 | } 99 | 100 | fn main() { 101 | let s = Square { 102 | x: 0.0f64, 103 | y: 0.0f64, 104 | side: 12.0f64, 105 | }; 106 | 107 | println!("Area of s: {}", s.area()); 108 | } 109 | ``` 110 | 111 | 对于`HasArea`和`Square`,我们声明了一个类型参数`T`并取代`f64`。`impl`则需要更深入的修改: 112 | 113 | ```rust 114 | impl HasArea for Square 115 | where T: Mul + Copy { ... } 116 | ``` 117 | 118 | `area`方法要求我们可以进行边的乘法,所以我们声明的`T`类型必须实现`std::ops::Mul`。比如上面提到的`Add`,`Mul`自身获取一个`Output`参数:因为我们知道相乘时数字并不会改变类型,我也设定它为`T`。`T`也必须支持拷贝,所以 Rust 并不尝试将`self.side`移动进返回值。 119 | -------------------------------------------------------------------------------- /content/`unsafe` 不安全代码.md: -------------------------------------------------------------------------------- 1 | # 不安全代码 2 | 3 | > [unsafe.md](https://github.com/rust-lang/book/blob/master/first-edition/src/unsafe.md) 4 | >
5 | > commit 98b18fabddaa942c6bbbb44509b7c00f4b07915f 6 | 7 | Rust 主要魅力是它强大的静态行为保障。不过安全检查天性保守:有些程序实际上是安全的,不过编译器不能验证它是否是真的。为了写这种类型的程序,我们需要告诉编译器稍微放松它的限制。为此,Rust 有一个关键字,`unsafe`。使用`unsafe`的代码比正常代码有更少的限制。 8 | 9 | 让我们过一遍语法,接着我们讨论语义。`unsafe`用在两个上下文中。第一个标记一个函数为不安全的: 10 | 11 | ```rust 12 | unsafe fn danger_will_robinson() { 13 | // Scary stuff... 14 | } 15 | ``` 16 | 17 | 例如所有从[FFI](Foreign Function Interface 外部函数接口.md)调用的函数都必须标记为`unsafe`。第二个`unsafe`的用途是一个不安全块。 18 | 19 | ```rust 20 | unsafe { 21 | // Scary stuff... 22 | } 23 | ``` 24 | 25 | 第三个是不安全 trait: 26 | 27 | ```rust 28 | unsafe trait Scary { } 29 | ``` 30 | 31 | 而第四个是`impl`这些 trait: 32 | 33 | ```rust 34 | # unsafe trait Scary { } 35 | unsafe impl Scary for i32 {} 36 | ``` 37 | 38 | 显式勾勒出那些可能会有 bug 并造成大问题的代码是很重要的。如果一个 Rust 程序段错误了,你可以确认它位于标记为`unsafe`部分的什么地方。 39 | 40 | ## “安全”指什么?(What does ‘safe’ mean?) 41 | 42 | 安全,在 Rust 的上下文中,意味着“不做任何不安全的事”。不过也要明白,有一些特定的行为在你的代码中可能并不合意,但很明显**并不是**不安全的: 43 | 44 | * 死锁 45 | * 内存或其他资源的泄露 46 | * 退出但未调用析构函数 47 | * 整型溢出 48 | 49 | Rust 不能避免所有类型的软件错误。有 bug 的代码可能并将会出现在 Rust 中。这些事并不很光彩,不过它们并不特别的定义为`unsafe`。 50 | 51 | 另外,如下列表全是 Rust 中的未定义行为,并且必须被避免,即便在编写`unsafe`代码时: 52 | 53 | * 数据竞争 54 | * 解引用一个空/悬垂裸指针 55 | * 读[`undef`](http://llvm.org/docs/LangRef.html#undefined-values)(未初始化)内存 56 | * 使用裸指针打破[指针重叠规则](http://llvm.org/docs/LangRef.html#pointer-aliasing-rules)(pointer aliasing rules) 57 | * `&mut T`和`&T`遵循LLVM范围的[`noalias`](http://llvm.org/docs/LangRef.html#noalias)模型,除了如果`&T`包含一个`UnsafeCell`的话。不安全代码必须不能违反这些重叠(aliasing)保证 58 | * 不使用`UnsafeCell`改变一个不可变值/引用 59 | * 通过编译器固有功能调用未定义行为: 60 | * 使用`std::ptr::offset`(`offset`功能)来索引超过对象边界的值,除了允许的末位超出一个字节 61 | * 在重叠(overlapping)缓冲区上使用`std::ptr::copy_nonoverlapping_memory`(`memcpy32/memcpy64`功能) 62 | * 原生类型的无效值,即使是在私有字段/本地变量中: 63 | * 空/悬垂引用或装箱 64 | * `bool`中一个不是`false`(`0`)或`true`(`1`)的值 65 | * `enum`中一个并不包含在类型定义中判别式 66 | * `char`中一个代理字(surrogate)或超过`char::MAX`的值 67 | * `str`中非UTF-8字节序列 68 | * 在外部代码中使用Rust或在Rust中使用外部语言 69 | 70 | ## 不安全的超级力量(Unsafe Superpowers) 71 | 72 | 在不安全函数和不安全块,Rust 将会让你做 3 件通常你不能做的事:只有 3 件。它们是: 73 | 74 | 1. 访问和更新一个[静态可变变量](`const` and `static`.md#static) 75 | 2. 解引用一个裸指针 76 | 3. 调用不安全函数。这是最 NB 的能力 77 | 78 | 这就是全部。注意到`unsafe`不能(例如)“关闭借用检查”是很重要的。为随机的Rust代码加上`unsafe`并不会改变它的语义,它并不会开始接受任何东西。 79 | 80 | 不过**确实**它会让你写的东西打破一些规则。让我们按顺序过一遍这3个能力。 81 | 82 | ### 访问和更新一个`static mut` 83 | 84 | Rust 有一个叫`static mut`的功能,它允许改变全局状态。这么做可能造成一个数据竞争,所以它天生是不安全的。关于更多细节,查看[静态量](`const` and `static`.md#static)部分。 85 | 86 | ### 解引用一个裸指针 87 | 88 | 裸指针让你做任意的指针算数,并会产生一系列不同的内存安全(safety & security)问题。在某种意义上,解引用一个任意指针的能力是你可以做的最危险的事之一。更多关于裸指针,查看[它的部分](Raw Pointers 裸指针.md)。 89 | 90 | ### 调用不安全函数 91 | 92 | 最后的能力能用于`unsafe`的两个方面:你只能在一个不安全块中调用被标记为`unsafe`的函数。 93 | 94 | 这个能力是强力和多变的。Rust 暴露了一些作为不安全函数的[编译器固有功能](Intrinsics 固有功能.md),并且一些不安全函数绕开了安全检查,用安全换速度。 95 | 96 | 我在重复一遍:即便你**可以**在一个不安全块和函数中做任何事并不意味你应该这么做。编译器会表现得像你在保持它不变一样(The compiler will act as though you’re upholding its invariants),所以请小心。 97 | -------------------------------------------------------------------------------- /content/Vectors.md: -------------------------------------------------------------------------------- 1 | # Vectors 2 | 3 | > [vectors.md](https://github.com/rust-lang/book/blob/master/first-edition/src/vectors.md) 4 | >
5 | > commit 23a7a7bdb6a6a43cd7efdd9176b1d3f75d9d0e70 6 | 7 | “Vector”是一个动态或“可增长”的数组,被实现为标准库类型[`Vec`](http://doc.rust-lang.org/std/vec/)(其中``是一个[泛型](Generics 泛型.md)语句)。vector 总是在堆上分配数据。vector 与切片就像`String`与`&str`一样。你可以使用`vec!`宏来创建它: 8 | 9 | ```rust 10 | let v = vec![1, 2, 3, 4, 5]; // v: Vec 11 | ``` 12 | 13 | (与之前使用`println!`宏时不一样,我们用中括号`[]`配合`vec!`。为了方便,Rust 允许使用上述各种情况。) 14 | 15 | 对于重复初始值有另一种形式的`vec!`: 16 | 17 | ```rust 18 | let v = vec![0; 10]; // ten zeroes 19 | ``` 20 | 21 | vector 将它们的内容以连续的`T`的数组的形式存储在堆上,这意味着它们必须在编译时就知道`T`的大小(就是存储一个`T`需要多少字节)。有些类型的大小不可能在编译时就知道。为此你需要保存一个指向该类型的指针:幸好,[`Box`](https://doc.rust-lang.org/std/boxed/)类型正好适合这种情况。 22 | 23 | ## 访问元素 24 | 25 | 为了vector特定索引的值,使用`[]`: 26 | 27 | ```rust 28 | let v = vec![1, 2, 3, 4, 5]; 29 | 30 | println!("The third element of v is {}", v[2]); 31 | ``` 32 | 33 | 索引从`0`开始,所以第 3 个元素是`v[2]`。 34 | 35 | 另外值得注意的是必须用`usize`类型的值来索引: 36 | 37 | ```rust 38 | let v = vec![1, 2, 3, 4, 5]; 39 | 40 | let i: usize = 0; 41 | let j: i32 = 0; 42 | 43 | // Works: 44 | v[i]; 45 | 46 | // Doesn’t: 47 | v[j]; 48 | ``` 49 | 50 | 用非`usize`类型索引的话会给出类似如下的错误: 51 | 52 | ```text 53 | error: the trait bound `collections::vec::Vec<_> : core::ops::Index` 54 | is not satisfied [E0277] 55 | v[j]; 56 | ^~~~ 57 | note: the type `collections::vec::Vec<_>` cannot be indexed by `i32` 58 | error: aborting due to previous error 59 | ``` 60 | 61 | 信息中有很多标点符号,不过关键是:你不能用`i32`来索引。 62 | 63 | ## 越界访问(Out-of-bounds Access) 64 | 65 | 如果尝试访问并不存在的索引: 66 | 67 | ```rust 68 | let v = vec![1, 2, 3]; 69 | println!("Item 7 is {}", v[7]); 70 | ``` 71 | 72 | 那么当前的线程会 [panic](Concurrency 并发.md#恐慌(panics))并输出如下信息: 73 | 74 | ```text 75 | thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 7' 76 | ``` 77 | 78 | 如果你想处理越界错误而不是 panic,你可以使用像[`get`](http://doc.rust-lang.org/std/vec/struct.Vec.html#method.get)或[`get_mut`](http://doc.rust-lang.org/std/vec/struct.Vec.html#method.get)这样的方法,他们当给出一个无效的索引时返回`None`: 79 | 80 | ```rust 81 | let v = vec![1, 2, 3]; 82 | match v.get(7) { 83 | Some(x) => println!("Item 7 is {}", x), 84 | None => println!("Sorry, this vector is too short.") 85 | } 86 | ``` 87 | 88 | ## 迭代 89 | 90 | 可以用`for`来迭代 vector 的元素。有3个版本: 91 | 92 | ```rust 93 | let mut v = vec![1, 2, 3, 4, 5]; 94 | 95 | for i in &v { 96 | println!("A reference to {}", i); 97 | } 98 | 99 | for i in &mut v { 100 | println!("A mutable reference to {}", i); 101 | } 102 | 103 | for i in v { 104 | println!("Take ownership of the vector and its element {}", i); 105 | } 106 | ``` 107 | 108 | 注意:你不能在使用 vector 的所有权遍历之后再次遍历它。你可以使用它的引用多次遍历 vector。例如,下面的代码不能编译。 109 | 110 | ```rust 111 | let v = vec![1, 2, 3, 4, 5]; 112 | 113 | for i in v { 114 | println!("Take ownership of the vector and its element {}", i); 115 | } 116 | 117 | for i in v { 118 | println!("Take ownership of the vector and its element {}", i); 119 | } 120 | ``` 121 | 122 | 而如下代码则可以完美运行: 123 | 124 | ```rust 125 | let v = vec![1, 2, 3, 4, 5]; 126 | 127 | for i in &v { 128 | println!("This is a reference to {}", i); 129 | } 130 | 131 | for i in &v { 132 | println!("This is a reference to {}", i); 133 | } 134 | ``` 135 | 136 | vector还有很多有用的方法,可以看看[vector的API文档](http://doc.rust-lang.org/nightly/std/vec/)了解它们。 137 | -------------------------------------------------------------------------------- /content/Advanced Linking 链接进阶.md: -------------------------------------------------------------------------------- 1 | # 链接进阶 2 | 3 | > [advanced-linking.md](https://github.com/rust-lang/rust/blob/stable/src/doc/book/advanced-linking.md) 4 | >
5 | > commit 226bcdf7d1e774f5967f92b0bd0bf237179f95c9 6 | 7 | Rust 的常用链接形式在本书的之前部分已经介绍过了,不过支持多种其他语言可用的可能的链接对 Rust 获取与原生库的无缝交互是很重要的。 8 | 9 | ## 链接参数(Link args) 10 | 11 | 这里还有一个方法来告诉 rustc 如何自定义链接,这就是通过`link_args`属性。这个属性作用于`extern`块并指定当产生构件时需要传递给连接器的原始标记。一个用例将是: 12 | 13 | ```rust,no_run 14 | #![feature(link_args)] 15 | 16 | #[link_args = "-foo -bar -baz"] 17 | extern {} 18 | # fn main() {} 19 | ``` 20 | 21 | 注意现在这个功能隐藏在`feature(link_args)`gate 之后因为它并不是一个被认可的执行链接的方法。目前 rustc 从 shell 调用系统的连接器(大多数系统是`gcc`,MSVC 是`link.exe`),所以使用额外的命令行参数是可行的,不过这并一定永远可行。将来 rustc 可能使用 LLVM 直接链接原生库这样一来`link_args`就毫无意义了。你可以向`rustc`传递`-C link-args`参数来获得和`link_args`属性同样的效果。 22 | 23 | 强烈建议你**不要**使用这个属性,而是使用一个更正式的`[link(...)]`属性作用于`extern`块。 24 | 25 | ## 静态链接 26 | 27 | 静态链接代表创建包含所有所需库的输出的过程,这样你在任何系统上使用你编译的项目时就不需要安装相应的库了。纯 Rust 的依赖默认都是静态链接的这样你可以使用你创建的二进制和库而不需要安装 Rust。相反,原生库(例如,`libc`和`libm`)通常是动态链接的,不过也可以修改为静态链接。 28 | 29 | 链接是一个非常依赖平台的问题--在一些平台上,静态链接可能根本就是不可能的!这个部分假设你对你选择的平台的链接一些基础的认识。 30 | 31 | ### Linux 32 | 33 | 在 Linux 上 Rust 程默认会链接系统的`libc`以及一些其他的库。让我们看看一个使用 GCC 和`glibc`的 64 位 Linux(目前为止 Linux 上最常见的`libc`)的例子: 34 | 35 | ```bash 36 | $ mkdir musldist 37 | $ PREFIX=$(pwd)/musldist 38 | $ 39 | $ # Build musl 40 | $ curl -O http://www.musl-libc.org/releases/musl-1.1.10.tar.gz 41 | $ tar xf musl-1.1.10.tar.gz 42 | $ cd musl-1.1.10/ 43 | musl-1.1.10 $ ./configure --disable-shared --prefix=$PREFIX 44 | musl-1.1.10 $ make 45 | musl-1.1.10 $ make install 46 | musl-1.1.10 $ cd .. 47 | $ du -h musldist/lib/libc.a 48 | 2.2M musldist/lib/libc.a 49 | $ 50 | $ # Build libunwind.a 51 | $ curl -O http://llvm.org/releases/3.7.0/llvm-3.7.0.src.tar.xz 52 | $ tar xf llvm-3.7.0.src.tar.xz 53 | $ cd llvm-3.7.0.src/projects/ 54 | llvm-3.7.0.src/projects $ curl http://llvm.org/releases/3.7.0/libunwind-3.7.0.src.tar.xz | tar xJf - 55 | llvm-3.7.0.src/projects $ mv libunwind-3.7.0.src libunwind 56 | llvm-3.7.0.src/projects $ mkdir libunwind/build 57 | llvm-3.7.0.src/projects $ cd libunwind/build 58 | llvm-3.7.0.src/projects/libunwind/build $ cmake -DLLVM_PATH=../../.. -DLIBUNWIND_ENABLE_SHARED=0 .. 59 | llvm-3.7.0.src/projects/libunwind/build $ make 60 | llvm-3.7.0.src/projects/libunwind/build $ cp lib/libunwind.a $PREFIX/lib/ 61 | llvm-3.7.0.src/projects/libunwind/build $ cd ../../../../ 62 | $ du -h musldist/lib/libunwind.a 63 | 164K musldist/lib/libunwind.a 64 | $ 65 | $ # Build musl-enabled rust 66 | $ git clone https://github.com/rust-lang/rust.git muslrust 67 | $ cd muslrust 68 | muslrust $ ./configure --target=x86_64-unknown-linux-musl --musl-root=$PREFIX --prefix=$PREFIX 69 | muslrust $ make 70 | muslrust $ make install 71 | muslrust $ cd .. 72 | $ du -h musldist/bin/rustc 73 | 12K musldist/bin/rustc 74 | ``` 75 | 76 | 现在你有了一个启用了`musl`的Rust!因为我们用了一个自定义的目录,当我们尝试并运行它的时候我们需要确保我们的系统能够找到二进制文件和正确的库: 77 | 78 | ```bash 79 | $ export PATH=$PREFIX/bin:$PATH 80 | $ export LD_LIBRARY_PATH=$PREFIX/lib:$LD_LIBRARY_PATH 81 | ``` 82 | 83 | 让我们试一下! 84 | 85 | ```bash 86 | $ echo 'fn main() { println!("hi!"); panic!("failed"); }' > example.rs 87 | $ rustc --target=x86_64-unknown-linux-musl example.rs 88 | $ ldd example 89 | not a dynamic executable 90 | $ ./example 91 | hi! 92 | thread 'main' panicked at 'failed', example.rs:1 93 | ``` 94 | 95 | 成功了!这个二进制文件可以被拷贝到几乎所有拥有相同构架的 Linux 机器上无故障的运行。 96 | 97 | `cargo build`也允许`--target`选项所以你也能用它来正常的构建你的 crate。然而,你可能需要先链接你的原生库到`musl`,在你可以链接到它之前。 98 | -------------------------------------------------------------------------------- /content/Benchmark tests 基准测试.md: -------------------------------------------------------------------------------- 1 | # 基准测试 2 | 3 | > [benchmark-tests.md](https://github.com/rust-lang/rust/blob/stable/src/doc/book/benchmark-tests.md) 4 | >
5 | > commit 28548db57d0acbc00ee80b43816953dbe31d53ba 6 | 7 | Rust 也支持基准测试,它可以测试代码的性能。让我们把`src/lib.rs`修改成这样(省略注释): 8 | 9 | ```rust 10 | #![feature(test)] 11 | 12 | extern crate test; 13 | 14 | pub fn add_two(a: i32) -> i32 { 15 | a + 2 16 | } 17 | 18 | #[cfg(test)] 19 | mod tests { 20 | use super::*; 21 | use test::Bencher; 22 | 23 | #[test] 24 | fn it_works() { 25 | assert_eq!(4, add_two(2)); 26 | } 27 | 28 | #[bench] 29 | fn bench_add_two(b: &mut Bencher) { 30 | b.iter(|| add_two(2)); 31 | } 32 | } 33 | ``` 34 | 35 | 注意`test`功能 gate,它启用了这个不稳定功能。 36 | 37 | 我们导入了`test`crate,它包含了对基准测试的支持。我们也定义了一个新函数,带有`bench`属性。与一般的不带参数的测试不同,基准测试有一个`&mut Bencher`参数。`Bencher`提供了一个`iter`方法,它接收一个闭包。这个闭包包含我们想要测试的代码。 38 | 39 | 我们可以用`cargo bench`来运行基准测试: 40 | 41 | ```bash 42 | $ cargo bench 43 | Compiling adder v0.0.1 (file:///home/steve/tmp/adder) 44 | Running target/release/adder-91b3e234d4ed382a 45 | 46 | running 2 tests 47 | test tests::it_works ... ignored 48 | test tests::bench_add_two ... bench: 1 ns/iter (+/- 0) 49 | 50 | test result: ok. 0 passed; 0 failed; 1 ignored; 1 measured 51 | ``` 52 | 53 | 我们的非基准测试将被忽略。你也许会发现`cargo bench`比`cargo test`花费的时间更长。这是因为Rust会多次运行我们的基准测试,然后取得平均值。因为我们的函数只做了非常少的操作,我们耗费了`1 ns/iter (+/- 0)`,不过运行时间更长的测试就会有出现偏差。 54 | 55 | 编写基准测试的建议: 56 | 57 | * 把初始代码放于`iter`循环之外,只把你想要测试的部分放入它 58 | * 确保每次循环都做了“同样的事情”,不要累加或者改变状态 59 | * 确保外边的函数也是幂等的(idempotent),基准测试runner可能会多次运行它 60 | * 确保`iter`循环内简短而快速,这样基准测试会运行的很快同时校准器可以在合适的分辨率上调整运转周期 61 | * 确保`iter`循环执行简单的工作,这样可以帮助我们准确的定位性能优化(或不足) 62 | 63 | # Gocha:优化 64 | 写基准测试有另一些比较微妙的地方:开启了优化编译的基准测试可能被优化器戏剧性的修改导致它不再是我们期望的基准测试了。举例来说,编译器可能认为一些计算并无外部影响并且整个移除它们。 65 | 66 | ```rust 67 | #![feature(test)] 68 | 69 | extern crate test; 70 | use test::Bencher; 71 | 72 | #[bench] 73 | fn bench_xor_1000_ints(b: &mut Bencher) { 74 | b.iter(|| { 75 | (0..1000).fold(0, |old, new| old ^ new); 76 | }); 77 | } 78 | ``` 79 | 80 | 得到如下结果: 81 | 82 | ```text 83 | running 1 test 84 | test bench_xor_1000_ints ... bench: 0 ns/iter (+/- 0) 85 | 86 | test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured 87 | ``` 88 | 89 | 基准测试运行器提供两种方法来避免这个问题:要么传递给`iter`的闭包可以返回一个随机的值这样强制优化器认为结果有用并确保它不会移除整个计算部分。这可以通过修改上面例子中的`b.iter`调用: 90 | 91 | ```rust 92 | # struct X; 93 | # impl X { fn iter(&self, _: F) where F: FnMut() -> T {} } let b = X; 94 | b.iter(|| { 95 | // Note lack of `;` (could also use an explicit `return`). 96 | (0..1000).fold(0, |old, new| old ^ new) 97 | }); 98 | ``` 99 | 100 | 要么,另一个选择是调用通用的`test::black_box`函数,它会传递给优化器一个不透明的“黑盒”这样强制它考虑任何它接收到的参数。 101 | 102 | ```rust 103 | #![feature(test)] 104 | 105 | extern crate test; 106 | 107 | # fn main() { 108 | # struct X; 109 | # impl X { fn iter(&self, _: F) where F: FnMut() -> T {} } let b = X; 110 | b.iter(|| { 111 | let n = test::black_box(1000); 112 | 113 | (0..n).fold(0, |a, b| a ^ b) 114 | }) 115 | # } 116 | ``` 117 | 上述两种方法均未读取或修改值,并且对于较小的值来说非常廉价。对于较大的值可以通过间接传递来减小额外开销(例如:`black_box(&huge_struct)`)。 118 | 119 | 执行上面任何一种修改可以获得如下基准测试结果: 120 | 121 | ```text 122 | running 1 test 123 | test bench_xor_1000_ints ... bench: 131 ns/iter (+/- 3) 124 | 125 | test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured 126 | ``` 127 | 128 | 然而,即使使用了上述方法优化器还是可能在不合适的情况下修改测试用例。 129 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | * [前言](preface.md) 3 | * [贡献者](CONTRIBUTING.md) 4 | * [1.介绍](content/README 介绍.md) 5 | * [2.准备](content/Getting Started 准备.md) 6 | * [3.教程:猜猜看](content/Guessing Game 猜猜看.md) 7 | * [4.语法和语义](content/Syntax and Semantics 语法和语义.md) 8 | * [4.1.变量绑定](content/Variable Bindings 变量绑定.md) 9 | * [4.2.函数](content/Functions 函数.md) 10 | * [4.3.原生类型](content/Primitive Types 原生类型.md) 11 | * [4.4.注释](content/Comments 注释.md) 12 | * [4.5.If语句](content/If If语句.md) 13 | * [4.6.循环](content/Loops 循环.md) 14 | * [4.7.Vectors](content/Vectors.md) 15 | * [4.8.所有权](content/Ownership 所有权.md) 16 | * [4.9.引用和借用](content/References and Borrowing 引用和借用.md) 17 | * [4.10.生命周期](content/Lifetimes 生命周期.md) 18 | * [4.11.可变性](content/Mutability 可变性.md) 19 | * [4.12.结构体](content/Structs 结构体.md) 20 | * [4.13.枚举](content/Enums 枚举.md) 21 | * [4.14.匹配](content/Match 匹配.md) 22 | * [4.15.模式](content/Patterns 模式.md) 23 | * [4.16.方法语法](content/Method Syntax 方法语法.md) 24 | * [4.17.字符串](content/Strings 字符串.md) 25 | * [4.18.泛型](content/Generics 泛型.md) 26 | * [4.19.Traits](content/Traits.md) 27 | * [4.20.Drop](content/Drop.md) 28 | * [4.21.if let](content/if let.md) 29 | * [4.22.trait 对象](content/Trait Objects trait 对象.md) 30 | * [4.23.闭包](content/Closures 闭包.md) 31 | * [4.24.通用函数调用语法](content/Universal Function Call Syntax 通用函数调用语法.md) 32 | * [4.25.crate 和模块](content/Crates and Modules crate 和模块.md) 33 | * [4.26.`const`和`static`](content/`const` and `static`.md) 34 | * [4.27.属性](content/Attributes 属性.md) 35 | * [4.28.`type`别名](content/`type` Aliases `type`别名.md) 36 | * [4.29.类型转换](content/Casting Between Types 类型转换.md) 37 | * [4.30.关联类型](content/Associated Types 关联类型.md) 38 | * [4.31.不定长类型](content/Unsized Types 不定长类型.md) 39 | * [4.32.运算符和重载](content/Operators and Overloading 运算符和重载.md) 40 | * [4.33.`Deref`强制多态](content/`Deref` coercions `Deref`强制多态.md) 41 | * [4.34.宏](content/Macros 宏.md) 42 | * [4.35.裸指针](content/Raw Pointers 裸指针.md) 43 | * [4.36.不安全代码](content/`unsafe` 不安全代码.md) 44 | * [5.高效 Rust](content/Effective Rust 高效 Rust.md) 45 | * [5.1.栈和堆](content/The Stack and the Heap 栈和堆.md) 46 | * [5.2.测试](content/Testing 测试.md) 47 | * [5.3.条件编译](content/Conditional Compilation 条件编译.md) 48 | * [5.4.文档](content/Documentation 文档.md) 49 | * [5.5.迭代器](content/Iterators 迭代器.md) 50 | * [5.6.并发](content/Concurrency 并发.md) 51 | * [5.7.错误处理](content/Error Handling 错误处理.md) 52 | * [5.8.选择你的保证](content/Choosing your Guarantees 选择你的保证.md) 53 | * [5.9.外部函数接口](content/Foreign Function Interface 外部函数接口.md) 54 | * [5.10.Borrow 和 AsRef](content/Borrow and AsRef Borrow 和 AsRef.md) 55 | * [5.11.发布途径](content/Release Channels 发布途径.md) 56 | * [5.12.不使用标准库开发 Rust](content/Using Rust Without the Standard Library 不使用标准库开发 Rust.md) 57 | * [5.13.过程宏(和自定义导出)](content/Procedural Macros 过程宏.md) 58 | * [6.Rust 开发版](content/Nightly Rust Rust 开发版.md) 59 | * [6.1.编译器插件](content/Compiler Plugins 编译器插件.md) 60 | * [6.2.内联汇编](content/Inline Assembly 内联汇编.md) 61 | * [6.3.不使用标准库](content/No stdlib 不使用标准库.md) 62 | * [6.4.固有功能](content/Intrinsics 固有功能.md) 63 | * [6.5.语言项](content/Lang items 语言项.md) 64 | * [6.6.链接进阶](content/Advanced Linking 链接进阶.md) 65 | * [6.7.基准测试](content/Benchmark tests 基准测试.md) 66 | * [6.8.装箱语法和模式](content/Box Syntax and Patterns 装箱语法和模式.md) 67 | * [6.9.切片模式](content/Slice patterns 切片模式.md) 68 | * [6.10.关联常量](content/Associated Constants 关联常量.md) 69 | * [6.11.自定义内存分配器](content/Custom Allocators 自定义内存分配器.md) 70 | * [7.词汇表](content/Glossary 词汇表.md) 71 | * [8.语法索引](content/Syntax Index 语法索引.md) 72 | * [9.参考文献](content/Bibliography 参考文献.md) 73 | * [附录:名词中英文对照](名词中英文对照.md) 74 | -------------------------------------------------------------------------------- /content/Mutability 可变性.md: -------------------------------------------------------------------------------- 1 | # 可变性 2 | 3 | > [mutability.md](https://github.com/rust-lang/book/blob/master/first-edition/src/mutability.md) 4 | >
5 | > commit 23a7a7bdb6a6a43cd7efdd9176b1d3f75d9d0e70 6 | 7 | 可变性,可以改变事物的能力,用在 Rust 中与其它语言有些许不同。可变性的第一方面是它并非默认状态: 8 | 9 | ```rust 10 | let x = 5; 11 | x = 6; // Error! 12 | ``` 13 | 14 | 我们可以使用`mut`关键字来引入可变性: 15 | 16 | ```rust 17 | let mut x = 5; 18 | 19 | x = 6; // No problem! 20 | ``` 21 | 22 | 这是一个可变的[变量绑定](Variable Bindings 变量绑定.md)。当一个绑定是可变的,它意味着你可以改变它指向的内容。所以在上面的例子中,`x`的值并没有多大的变化,不过这个绑定从一个`i32`变成了另外一个。 23 | 24 | 你也可以使用`&x`创建一个它的[引用](References and Borrowing 引用和借用.md),不过如果你想要使用这个引用来改变它的值,你将会需要一个[可变引用](References and Borrowing 引用和借用.md): 25 | 26 | ```rust 27 | let mut x = 5; 28 | let y = &mut x; 29 | ``` 30 | 31 | `y`是一个(指向)可变引用的不可变绑定,它意味着你不能把`y`与其它变量绑定(`y = &mut z`),不过`y`可以用来把`x`绑定到别的值上(`*y = 5`)。一个微妙的区别。 32 | 33 | 当然,如果你想它们都可变: 34 | 35 | ```rust 36 | let mut x = 5; 37 | let mut y = &mut x; 38 | ``` 39 | 40 | 现在`y`可以绑定到另外一个值,并且它引用的值也可以改变。 41 | 42 | 很重要的一点是`mut`是[模式](Patterns 模式.md)的一部分,所以你可以这样做: 43 | 44 | ```rust 45 | let (mut x, y) = (5, 6); 46 | 47 | fn foo(mut x: i32) { 48 | # } 49 | ``` 50 | 51 | 注意这里`x`是可变的,`y`不是。 52 | 53 | ## 内部可变性 VS 外部可变性(Interior vs. Exterior Mutability) 54 | 55 | 然而,当我们谈到 Rust 中什么是“不可变”的时候,它并不意味着它不能被改变:这里是指它的“外部可变性”是不可变的。例如,考虑下[`Arc`](http://doc.rust-lang.org/nightly/std/sync/struct.Arc.html): 56 | 57 | ```rust 58 | use std::sync::Arc; 59 | 60 | let x = Arc::new(5); 61 | let y = x.clone(); 62 | ``` 63 | 64 | 当我们调用`clone()`时,`Arc`需要更新引用计数。然而你并未使用任何`mut`,`x`是一个不可变绑定,并且我们也没有取得`&mut 5`或者什么。那么发生了什么呢? 65 | 66 | 为了解释这些,我们不得不回到Rust指导哲学的核心,内存安全,和Rust用以保证它的机制,[所有权](Ownership 所有权.md)系统,和更具体的[借用](Borrow and AsRef Borrow 和 AsRef.md#borrow): 67 | 68 | > 你可以拥有这两种类型借用的其中一个,但不能同时拥有: 69 | > 70 | > * 拥有 1 个或多个不可变引用(&T) 71 | > * 只有 1 个可变引用(&mut T) 72 | 73 | 因此,这就是“不可变性”的真正定义:当有两个引用指向同一事物是安全的吗?在`Arc`的情况下,是安全的:改变完全包含在结构自身内部。它并不面向用户。为此,它用`clone()`分配`&T`。如果分配`&mut T`的话,那么,这将会是一个问题。 74 | 75 | 其它类型,像[std::cell](http://doc.rust-lang.org/nightly/std/cell/)模块中的这一个,则有相反的属性:内部可变性。例如: 76 | 77 | ```rust 78 | use std::cell::RefCell; 79 | 80 | let x = RefCell::new(42); 81 | 82 | let y = x.borrow_mut(); 83 | ``` 84 | 85 | `RefCell`使用`borrow_mut()`方法来分配它内部资源的`&mut`引用。这难道不危险吗?如果我们: 86 | 87 | ```rust 88 | use std::cell::RefCell; 89 | 90 | let x = RefCell::new(42); 91 | 92 | let y = x.borrow_mut(); 93 | let z = x.borrow_mut(); 94 | # (y, z); 95 | ``` 96 | 97 | 事实上这会在运行时引起恐慌。`RefCell`是这样工作的:它在运行时强制使用Rust的借用规则,并且如果有违反就会`panic!`。这让我们绕开了Rust可变性规则的另一方面。让我们先讨论一下它。 98 | 99 | ## 字段级别可变性(Field-level mutability) 100 | 101 | 可变性是借用(`&mut`)或者绑定(`let mut`)的属性之一。这意味着,例如,你不能让一个[结构体](Structs 结构体.md)的一些字段可变而另一些字段不可变: 102 | 103 | ```rust 104 | struct Point { 105 | x: i32, 106 | mut y: i32, // Nope. 107 | } 108 | ``` 109 | 110 | 结构体的可变性位于它的绑定上: 111 | 112 | ```rust 113 | struct Point { 114 | x: i32, 115 | y: i32, 116 | } 117 | 118 | let mut a = Point { x: 5, y: 6 }; 119 | 120 | a.x = 10; 121 | 122 | let b = Point { x: 5, y: 6}; 123 | 124 | b.x = 10; // Error: cannot assign to immutable field `b.x` 125 | ``` 126 | 127 | 然而,通过使用[`Cell`][cell],你可以模拟字段级别的可变性: 128 | 129 | ```rust 130 | use std::cell::Cell; 131 | 132 | struct Point { 133 | x: i32, 134 | y: Cell, 135 | } 136 | 137 | let point = Point { x: 5, y: Cell::new(6) }; 138 | 139 | point.y.set(7); 140 | 141 | println!("y: {:?}", point.y); 142 | ``` 143 | 144 | [cell]: http://doc.rust-lang.org/std/cell/struct.Cell.html 145 | 146 | 这会打印`y: Cell { value: 7 }`。我们成功的更新了`y`。 147 | -------------------------------------------------------------------------------- /content/No stdlib 不使用标准库.md: -------------------------------------------------------------------------------- 1 | # 不使用标准库 2 | 3 | > [no-stdlib.md](https://github.com/rust-lang/rust/blob/stable/src/doc/book/no-stdlib.md) 4 | >
5 | > commit 893f42a83466cf02b6fd6d3c82d5419cdad47474 6 | 7 | Rust 的标准库提供了很多有用的功能,不过它假设它的 host 系统的多种功能的支持:线程,网络,堆分配和其他功能。有些系统并没有这些功能,不过,Rust也能在这些系统上工作。为此,我们可以通过一个属性来告诉 Rust 我们不想使用标准库:`#![no_std]`。 8 | 9 | > 注意:这个功能技术上是稳定的,不过有些附加条件。其一,你可以构建一个稳定的`#![no_std]`库,但二进制文件不行。关于没有标准库的库文件的细节,查看[关于`#![no_std]`的章节](https://github.com/rust-lang/rust/blob/master/src/doc/book/using-rust-without-the-standard-library.html)。 10 | 11 | 很显然你并不一定需要标准库:可以使用`#[no_std`来构建一个可执行程序。 12 | 13 | ## 使用 libc 14 | 15 | 为了构建一个`#[no_std]`可执行程序,我们需要 libc 作为依赖。可以在`Cargo.toml`文件中指定: 16 | 17 | ```toml 18 | [dependencies] 19 | libc = { version = "0.2.14", default-features = false } 20 | ``` 21 | 22 | 注意默认功能被禁用了。这是关键的一步————**libc 的默认功能引用了标准库所以必须被禁用。** 23 | 24 | ## 不用标准库编写可执行程序 25 | 26 | 有两种可能的控制入口点的方法:`#[start]`属性,或者用你自己的代码 override C `main`函数的默认 shim。 27 | 28 | 被标记为`#[start]`的函数传递的参数格式与 C 一致: 29 | 30 | ```rust 31 | #![feature(lang_items, core_intrinsics)] 32 | #![feature(start)] 33 | #![no_std] 34 | use core::intrinsics; 35 | 36 | // Pull in the system libc library for what crt0.o likely requires. 37 | extern crate libc; 38 | 39 | // Entry point for this program. 40 | #[start] 41 | fn start(_argc: isize, _argv: *const *const u8) -> isize { 42 | 0 43 | } 44 | 45 | // These functions are used by the compiler, but not 46 | // for a bare-bones hello world. These are normally 47 | // provided by libstd. 48 | #[lang = "eh_personality"] 49 | #[no_mangle] 50 | pub extern fn rust_eh_personality() { 51 | } 52 | 53 | // This function may be needed based on the compilation target. 54 | #[lang = "eh_unwind_resume"] 55 | #[no_mangle] 56 | pub extern fn rust_eh_unwind_resume() { 57 | } 58 | 59 | #[lang = "panic_fmt"] 60 | #[no_mangle] 61 | pub extern fn rust_begin_panic(_msg: core::fmt::Arguments, 62 | _file: &'static str, 63 | _line: u32) -> ! { 64 | unsafe { intrinsics::abort() } 65 | } 66 | ``` 67 | 68 | 要 override 编译器插入的`main` shim,你必须使用`#![no_main]`禁用它并通过正确的 ABI 和正确的名字来创建合适的函数,这也需要需要覆盖编译器的命名改编: 69 | 70 | ```rust 71 | #![feature(lang_items, core_intrinsics)] 72 | #![feature(start)] 73 | #![no_std] 74 | #![no_main] 75 | use core::intrinsics; 76 | 77 | // Pull in the system libc library for what crt0.o likely requires. 78 | extern crate libc; 79 | 80 | // Entry point for this program. 81 | #[no_mangle] // ensure that this symbol is called `main` in the output 82 | pub extern fn main(_argc: i32, _argv: *const *const u8) -> i32 { 83 | 0 84 | } 85 | 86 | // These functions are used by the compiler, but not 87 | // for a bare-bones hello world. These are normally 88 | // provided by libstd. 89 | #[lang = "eh_personality"] 90 | #[no_mangle] 91 | pub extern fn rust_eh_personality() { 92 | } 93 | 94 | // This function may be needed based on the compilation target. 95 | #[lang = "eh_unwind_resume"] 96 | #[no_mangle] 97 | pub extern fn rust_eh_unwind_resume() { 98 | } 99 | 100 | #[lang = "panic_fmt"] 101 | #[no_mangle] 102 | pub extern fn rust_begin_panic(_msg: core::fmt::Arguments, 103 | _file: &'static str, 104 | _line: u32) -> ! { 105 | unsafe { intrinsics::abort() } 106 | } 107 | ``` 108 | 109 | ## 关于 language items 的更多细节 110 | 111 | 目前编译器对能够被可执行文件调用的符号做了一些假设。正常情况下,这些函数是由标准库提供的,不过没有它你就必须定义你自己的了。这些符号被称为“language items”,并且他们每个都有一个内部的名称,和一个必须符合签名的实现。 112 | 113 | 这些函数中的第一个,`eh_personality`,被编译器的错误机制使用。它通常映射到 GCC 的特性函数上(查看[libstd实现](https://github.com/rust-lang/rust/blob/master/src/libpanic_unwind/gcc.rs)来获取更多信息),不过对于不会触发恐慌的包装箱可以确定这个函数不会被调用。language item 的名称是`eh_personality`。 114 | 115 | 第二个函数,`rust_begin_panic`,也被作为编译器的错误机制使用。当发生 panic 时,它控制显示在屏幕上的信息。虽然 language item 的名称是`panic_fmt`,但是符号的名称是`rust_begin_panic`。 116 | 117 | 第三个函数,`rust_eh_unwind_resume`,在target 选项中的`custom_unwind_resume` flag 被设置时也是必需的。它允许自定义在 landing pads 的最后的 resuming unwind 过程。language item 的名字是`eh_unwind_resume`。 118 | -------------------------------------------------------------------------------- /content/Loops 循环.md: -------------------------------------------------------------------------------- 1 | # 循环 2 | 3 | > [loops.md](https://github.com/rust-lang/book/blob/master/first-edition/src/loops.md) 4 | >
5 | > commit 549270d4e66cc77b1f02cea13aaad9c90f150524 6 | 7 | Rust 目前提供 3 种方法来进行一些迭代操作。他们是`loop`,`while`和`for`。每种方法都有自己的用途。 8 | 9 | ## loop 10 | 11 | 无限`loop`是 Rust 提供的最简单的循环。使用`loop`关键字,Rust 提供了一个直到一些终止语句被执行的循环方法。Rust 的无限`loop`看起来像这样: 12 | 13 | ```rust 14 | loop { 15 | println!("Loop forever!"); 16 | } 17 | ``` 18 | 19 | ## while 20 | 21 | Rust 也有一个`while`循环。它看起来像: 22 | 23 | ```rust 24 | let mut x = 5; // mut x: i32 25 | let mut done = false; // mut done: bool 26 | 27 | while !done { 28 | x += x - 3; 29 | 30 | println!("{}", x); 31 | 32 | if x % 5 == 0 { 33 | done = true; 34 | } 35 | } 36 | ``` 37 | 38 | `while`循环是当你不确定应该循环多少次时正确的选择。 39 | 40 | 如果你需要一个无限循环,你可能想要这么写: 41 | 42 | ```rust 43 | while true { 44 | ``` 45 | 46 | 然而,`loop`远比它适合处理这个情况: 47 | 48 | ```rust 49 | loop { 50 | ``` 51 | 52 | Rust 的控制流分析会区别对待这个与`while true`,因为我们知道它会一直循环。现阶段理解这些细节**意味着**什么并不是非常重要,基本上,你给编译器越多的信息,越能确保安全和生成更好的代码,所以当你打算无限循环的时候应该总是倾向于使用`loop`。 53 | 54 | ## for 55 | 56 | `for`用来循环一个特定的次数。然而,Rust的`for`循环与其它系统语言有些许不同。Rust的`for`循环看起来并不像这个“C语言样式”的`for`循环: 57 | 58 | ```c 59 | for (x = 0; x < 10; x++) { 60 | printf( "%d\n", x ); 61 | } 62 | ``` 63 | 64 | 相反,它看起来像这个样子: 65 | 66 | ```rust 67 | for x in 0..10 { 68 | println!("{}", x); // x: i32 69 | } 70 | ``` 71 | 72 | 更抽象的形式: 73 | 74 | ```rust 75 | for var in expression { 76 | code 77 | } 78 | ``` 79 | 80 | 这个表达式是一个[迭代器](Iterators 迭代器.md).迭代器返回一系列的元素。每次迭代循环中的一个元素。然后它的值与`var`绑定,它在循环体中有效。每当循环体执行完后,我们从迭代器中取出下一个值,然后我们再重复一遍。当迭代器中不再有值时,`for`循环结束。 81 | 82 | 在我们的例子中,`0..10`表达式取一个开始和结束的位置,然后给出一个含有这之间值得迭代器。当然它不包括上限值,所以我们的循环会打印`0`到`9`,而不是到`10`。 83 | 84 | Rust 没有使用“C语言风格”的`for`循环是有意为之的。即使对于有经验的 C 语言开发者来说,要手动控制要循环的每个元素也都是复杂并且易于出错的。 85 | 86 | ## Enumerate 方法 87 | 88 | 当你需要记录你已经循环了多少次了的时候,你可以使用`.enumerate()`函数。 89 | 90 | ### 对于范围(On ranges): 91 | 92 | ```rust 93 | for (index, value) in (5..10).enumerate() { 94 | println!("index = {} and value = {}", index, value); 95 | } 96 | ``` 97 | 98 | 输出: 99 | 100 | ```text 101 | index = 0 and value = 5 102 | index = 1 and value = 6 103 | index = 2 and value = 7 104 | index = 3 and value = 8 105 | index = 4 and value = 9 106 | ``` 107 | 108 | 别忘了在范围外面加上括号。 109 | 110 | ### 对于迭代器(On iterators): 111 | 112 | ```rust 113 | let lines = "hello\nworld".lines(); 114 | 115 | for (linenumber, line) in lines.enumerate() { 116 | println!("{}: {}", linenumber, line); 117 | } 118 | ``` 119 | 120 | 输出: 121 | 122 | ```text 123 | 0: hello 124 | 1: world 125 | ``` 126 | ## 提早结束迭代(Ending iteration early) 127 | 128 | 让我们再看一眼之前的`while`循环: 129 | 130 | ```rust 131 | let mut x = 5; 132 | let mut done = false; 133 | 134 | while !done { 135 | x += x - 3; 136 | 137 | println!("{}", x); 138 | 139 | if x % 5 == 0 { 140 | done = true; 141 | } 142 | } 143 | ``` 144 | 145 | 我们必须使用一个`mut`布尔型变量绑定,`done`,来确定何时我们应该推出循环。Rust 有两个关键字帮助我们来修改迭代:`break`和`continue`。 146 | 147 | 这样,我们可以用`break`来写一个更好的循环: 148 | 149 | ```rust 150 | let mut x = 5; 151 | 152 | loop { 153 | x += x - 3; 154 | 155 | println!("{}", x); 156 | 157 | if x % 5 == 0 { break; } 158 | } 159 | ``` 160 | 161 | 现在我们用`loop`来无限循环,然后用`break`来提前退出循环。 162 | 163 | `continue`比较类似,不过不是退出循环,它直接进行下一次迭代。下面的例子只会打印奇数: 164 | 165 | ```rust 166 | for x in 0..10 { 167 | if x % 2 == 0 { continue; } 168 | 169 | println!("{}", x); 170 | } 171 | ``` 172 | 173 | `break`和`continue`在`while`循环和[`for`循环](#for)中都有效。 174 | 175 | ## 循环标签(Loop labels) 176 | 177 | 你也许会遇到这样的情形,当你有嵌套的循环而希望指定你的哪一个`break`或`continue`该起作用。就像大多数语言,默认`break`或`continue`将会作用于最内层的循环。当你想要一个`break`或`continue`作用于一个外层循环,你可以使用标签来指定你的`break`或`continue`语句作用的循环。如下代码只会在`x`和`y`都为奇数时打印他们: 178 | 179 | ```rust 180 | 'outer: for x in 0..10 { 181 | 'inner: for y in 0..10 { 182 | if x % 2 == 0 { continue 'outer; } // Continues the loop over `x`. 183 | if y % 2 == 0 { continue 'inner; } // Continues the loop over `y`. 184 | println!("x: {}, y: {}", x, y); 185 | } 186 | } 187 | ``` 188 | -------------------------------------------------------------------------------- /content/Generics 泛型.md: -------------------------------------------------------------------------------- 1 | # 泛型 2 | 3 | > [generics.md](https://github.com/rust-lang/book/blob/master/first-edition/src/generics.md) 4 | >
5 | > commit 23a7a7bdb6a6a43cd7efdd9176b1d3f75d9d0e70 6 | 7 | 有时,当你编写函数或数据类型时,我们可能会希望它能处理多种类型的参数。幸运的是,Rust有一个能给我们更好选择的功能:泛型。泛型在类型理论中叫做**参数多态**(*parametric polymorphism*),它意味着它们是对于给定参数(parametric)能够有多种形式(`poly`是多,`morph`是形态)的函数或类型。 8 | 9 | 不管怎么样,类型理论就说这么多,现在我们来看些泛型代码。Rust 标准库提供了一个范型的类型——`Option`: 10 | 11 | ```rust 12 | enum Option { 13 | Some(T), 14 | None, 15 | } 16 | ``` 17 | 18 | 之前你已见过几次的``部分代表它是一个泛型数据类型。在上面的枚举声明中,每当我们看到`T`,我们用这个类型代替我们泛型中使用的类型。下面是一个使用`Option`的例子,它带有额外的类型标注: 19 | 20 | ```rust 21 | let x: Option = Some(5); 22 | ``` 23 | 24 | 在类型声明中,我们看到`Option`。注意它与`Option`的相似之处。在这个特定的`Option`中,`T`的值为`i32`。在绑定的右侧,我们用了`Some(T)`,其中`T`是`5`。因为它是`i32`型的,两边类型相符,所以皆大欢喜。如果不相符,我们会得到一个错误: 25 | 26 | ```rust 27 | let x: Option = Some(5); 28 | // error: mismatched types: expected `core::option::Option`, 29 | // found `core::option::Option<_>` (expected f64 but found integral variable) 30 | ``` 31 | 32 | 这并不意味着我们不能写用`f64`的`Option`!只是类型必须相符: 33 | 34 | ```rust 35 | let x: Option = Some(5); 36 | let y: Option = Some(5.0f64); 37 | ``` 38 | 39 | 这样就好了。一处定义,到处使用。 40 | 41 | 不一定只有一个类型是泛型的。想想Rust标准库中另一个类似的`Result`类型: 42 | 43 | ```rust 44 | enum Result { 45 | Ok(T), 46 | Err(E), 47 | } 48 | ``` 49 | 50 | 这里有两个泛型类型:`T`和`E`。另外,大写字母可以是任何你喜欢的(大写)字母。我们可以定义`Result`为: 51 | 52 | ```rust 53 | enum Result { 54 | Ok(A), 55 | Err(Z), 56 | } 57 | ``` 58 | 59 | 如果你想这么做的话。惯例告诉我们第一个泛型参数应该是`T`,代表`type`,然后我们用`E`来代表`error`。然而,Rust 并不管这些。 60 | 61 | `Result`意图作为计算的返回值,并为了能够在不能工作时返回一个错误。 62 | 63 | ## 泛型函数 64 | 65 | 我们可以用熟悉的语法编写一个获取泛型参数的函数: 66 | 67 | ```rust 68 | fn takes_anything(x: T) { 69 | // Do something with `x`. 70 | } 71 | ``` 72 | 73 | 语法有两部分:``代表“这个函数带有一个泛型类型”,而`x: T`代表“`x`是`T`类型的”。 74 | 75 | 多个参数可以有相同的泛型类型: 76 | 77 | ```rust 78 | fn takes_two_of_the_same_things(x: T, y: T) { 79 | // ... 80 | } 81 | ``` 82 | 83 | 我们可以写一个获取多个(泛型)类型的版本: 84 | 85 | ```rust 86 | fn takes_two_things(x: T, y: U) { 87 | // ... 88 | } 89 | ``` 90 | 91 | ## 泛型结构体(Generic structs) 92 | 93 | 你也可以在一个`struct`中储存泛型类型: 94 | 95 | ```rust 96 | struct Point { 97 | x: T, 98 | y: T, 99 | } 100 | 101 | let int_origin = Point { x: 0, y: 0 }; 102 | let float_origin = Point { x: 0.0, y: 0.0 }; 103 | ``` 104 | 105 | 与函数类似,``是我们声明的泛型参数,而我们也接着在类型定义中使用`x: T`。 106 | 107 | 当你想要给泛型`struct`增加一个实现时,你可以在`impl`声明类型参数: 108 | 109 | ```rust 110 | # struct Point { 111 | # x: T, 112 | # y: T, 113 | # } 114 | # 115 | impl Point { 116 | fn swap(&mut self) { 117 | std::mem::swap(&mut self.x, &mut self.y); 118 | } 119 | } 120 | ``` 121 | 122 | 目前为止你已经见过了支持几乎任何类型的泛型。他们在很多地方都是有用的:你已经见过了`Option`,接下来你还将见到像[`Vec`](http://doc.rust-lang.org/std/vec/struct.Vec.html)这样的通用容器类型。另一方面,通常你想要用灵活性去换取更强的表现力。阅读[trait bound](Traits.md#泛型函数的-trait-bound(trait-bounds-on-generic-functions))章节来了解为什么和如何做。 123 | 124 | ## 消除歧义(Resolving ambiguities) 125 | 126 | 大部分时候当涉及到泛型时,编译器可以自动推断出泛型参数: 127 | 128 | ```rust 129 | // v must be a Vec but we don't know what T is yet 130 | let mut v = Vec::new(); 131 | // v just got a bool value, so T must be bool! 132 | v.push(true); 133 | // Debug-print v 134 | println!("{:?}", v); 135 | ``` 136 | 137 | 但是有的时候,编译器需要一些帮助。例如,如下如果省略最后一行的打印,会得到一个编译错误: 138 | 139 | ```rust,ignore 140 | let v = Vec::new(); 141 | // ^^^^^^^^ cannot infer type for `T` 142 | // 143 | // note: type annotations or generic parameter binding required 144 | println!("{:?}", v); 145 | ``` 146 | 147 | 我们要么可以使用一个类型注解来解决它: 148 | 149 | ```rust 150 | let v: Vec = Vec::new(); 151 | println!("{:?}", v); 152 | ``` 153 | 154 | 要么通过一个叫做[‘turbofish’][turbofish] `::<>` 的语法来绑定泛型参数`T`: 155 | 156 | ```rust 157 | let v = Vec::::new(); 158 | println!("{:?}", v); 159 | ``` 160 | 161 | 第二种方法在我们并不想要将结果绑定到一个变量时很有用。它也可以用来在函数和方法中绑定泛型参数。查看[迭代器与消费者](iterators.html#consumers) 章节来获取一个例子。 162 | 163 | [turbofish]: http://doc.rust-lang.org/std/iter/trait.Iterator.html#method.collect -------------------------------------------------------------------------------- /content/Strings 字符串.md: -------------------------------------------------------------------------------- 1 | # 字符串 2 | 3 | > [strings.md](https://github.com/rust-lang/book/blob/master/first-edition/src/strings.md) 4 | >
5 | > commit 23a7a7bdb6a6a43cd7efdd9176b1d3f75d9d0e70 6 | 7 | 对于每一个程序,字符串都是需要掌握的重要内容。由于Rust主要着眼于系统编程,所以它的字符串处理系统与其它语言有些许区别。每当你碰到一个可变大小的数据结构时,情况都会变得很微妙,而字符串正是可变大小的数据结构。这也就是说,Rust的字符串与一些像C这样的系统编程语言也不相同。 8 | 9 | 让我们进一步了解一下。一个**字符串**是一串UTF-8字节编码的Unicode量级值的序列。所有的字符串都确保是有效编码的UTF-8序列。另外,字符串并不以null结尾并且可以包含null字节。 10 | 11 | Rust有两种主要的字符串类型:`&str`和`String`。让我们先看看`&str`。这叫做**字符串片段**(*string slices*)。字符串常量是`&'static str`类型的: 12 | 13 | ```rust 14 | let greeting = "Hello there."; // greeting: &'static str 15 | ``` 16 | 17 | `"Hello there."`是一个字符串常量而它的类型是`&'static str`。字符串常量是静态分配的字符串切片,也就是说它储存在我们编译好的程序中,并且整个程序的运行过程中一直存在。这个`greeting`绑定了一个静态分配的字符串的引用。任何接受一个字符串切片的函数也接受一个字符串常量。 18 | 19 | 字符串常量可以跨多行。有两种形式。第一种会包含新行符和之前的空格: 20 | 21 | ```rust 22 | let s = "foo 23 | bar"; 24 | 25 | assert_eq!("foo\n bar", s); 26 | ``` 27 | 28 | 第二种,带有`\`,会去掉空格和新行符: 29 | 30 | ```rust 31 | let s = "foo\ 32 | bar"; 33 | 34 | assert_eq!("foobar", s); 35 | ``` 36 | 37 | 注意通常你不能直接访问一个`str`,只能通过`&str`引用。这是因为`str`是一个不定长类型,它需要额外的运行时信息才能使用。关于更多请查看[不定长类型章节](Unsized Types 不定长类型.md)。 38 | 39 | Rust 当然不仅仅只有`&str`。一个`String`,是一个在堆上分配的字符串。这个字符串可以增长,并且也保证是UTF-8编码的。`String`通常通过一个字符串片段调用`to_string`方法转换而来。 40 | 41 | ```rust 42 | let mut s = "Hello".to_string(); // mut s: String 43 | println!("{}", s); 44 | 45 | s.push_str(", world."); 46 | println!("{}", s); 47 | ``` 48 | 49 | `String`可以通过一个`&`强制转换为`&str`: 50 | 51 | ```rust 52 | fn takes_slice(slice: &str) { 53 | println!("Got: {}", slice); 54 | } 55 | 56 | fn main() { 57 | let s = "Hello".to_string(); 58 | takes_slice(&s); 59 | } 60 | ``` 61 | 62 | 这种强制转换并不发生在接受`&str`的trait而不是`&str`本身作为参数的函数上。例如,[TcpStream::connect](http://doc.rust-lang.org/stable/std/net/struct.TcpStream.html#method.connect),有一个`ToSocketAddrs`类型的参数。`&str`可以不用转换不过`String`必须使用`&*`显式转换。 63 | 64 | ```rust 65 | use std::net::TcpStream; 66 | 67 | TcpStream::connect("192.168.0.1:3000"); // Parameter is of type &str. 68 | 69 | let addr_string = "192.168.0.1:3000".to_string(); 70 | TcpStream::connect(&*addr_string); // Convert `addr_string` to &str. 71 | ``` 72 | 73 | 把`String`转换为`&str`的代价很小,不过从`&str`转换到`String`涉及到分配内存。除非必要,没有理由这样做! 74 | 75 | ## 索引(Indexing) 76 | 77 | 因为字符串是有效 UTF-8 编码的,它不支持索引: 78 | 79 | ```rust 80 | let s = "hello"; 81 | 82 | println!("The first letter of s is {}", s[0]); // ERROR!!! 83 | ``` 84 | 85 | 通常,用`[]`访问一个数组是非常快的。不过,字符串中每个UTF-8编码的字符可以是多个字节,你必须遍历字符串来找到字符串的第N个字符。这个操作的代价相当高,而且我们不想误导读者。更进一步来讲,Unicode实际上并没有定义什么“字符”。我们可以选择把字符串看作一个串独立的字节,或者代码点(codepoints): 86 | 87 | ```rust 88 | let hachiko = "忠犬ハチ公"; 89 | 90 | for b in hachiko.as_bytes() { 91 | print!("{}, ", b); 92 | } 93 | 94 | println!(""); 95 | 96 | for c in hachiko.chars() { 97 | print!("{}, ", c); 98 | } 99 | 100 | println!(""); 101 | ``` 102 | 103 | 这会打印出: 104 | 105 | ```text 106 | 229, 191, 160, 231, 138, 172, 227, 131, 143, 227, 131, 129, 229, 133, 172, 107 | 忠, 犬, ハ, チ, 公, 108 | ``` 109 | 110 | 如你所见,这有比`char`更多的字节。 111 | 112 | 你可以这样来获取跟索引相似的东西: 113 | 114 | ```rust 115 | # let hachiko = "忠犬ハチ公"; 116 | let dog = hachiko.chars().nth(1); // Kinda like `hachiko[1]`. 117 | ``` 118 | 119 | 这强调了我们不得不遍历整个`char`的列表。 120 | 121 | ## 切片(Slicing) 122 | 123 | 你可以使用切片语法来获取一个字符串的切片: 124 | 125 | ```rust 126 | let dog = "hachiko"; 127 | let hachi = &dog[0..5]; 128 | ``` 129 | 130 | 注意这里是**字节**偏移,而不是**字符**偏移。所以如下代码在运行时会失败: 131 | 132 | ```rust 133 | let dog = "忠犬ハチ公"; 134 | let hachi = &dog[0..2]; 135 | ``` 136 | 137 | 给出如下错误: 138 | 139 | ```text 140 | thread 'main' panicked at 'byte index 2 is not a char boundary; it is inside '忠' 141 | (bytes 0..3) of `忠犬ハチ公`' 142 | ``` 143 | 144 | ## 连接(Concatenation) 145 | 146 | 如果你有一个`String`,你可以在它后面接上一个`&str`: 147 | 148 | ```rust 149 | let hello = "Hello ".to_string(); 150 | let world = "world!"; 151 | 152 | let hello_world = hello + world; 153 | ``` 154 | 155 | 不过如果你有两个`String`,你需要一个`&`: 156 | 157 | ```rust 158 | let hello = "Hello ".to_string(); 159 | let world = "world!".to_string(); 160 | 161 | let hello_world = hello + &world; 162 | ``` 163 | 164 | 这是因为`&String`可以自动转换为一个`&str`。这个功能叫做[`Deref`转换](`Deref` coercions `Deref`强制多态.md)。 165 | -------------------------------------------------------------------------------- /content/Inline Assembly 内联汇编.md: -------------------------------------------------------------------------------- 1 | # 内联汇编 2 | 3 | > [inline-assembly.md](https://github.com/rust-lang/rust/blob/stable/src/doc/book/inline-assembly.md) 4 | >
5 | > commit 28548db57d0acbc00ee80b43816953dbe31d53ba 6 | 7 | 为了极端底层操作和性能要求,你可能希望直接控制 CPU。Rust 通过`asm!`宏来支持使用内联汇编。 8 | 9 | ```rust 10 | asm!(assembly template 11 | : output operands 12 | : input operands 13 | : clobbers 14 | : options 15 | ); 16 | ``` 17 | 18 | 任何`asm`的使用需要功能通道(需要在包装箱上加上`#![feature(asm)]`来允许使用)并且当然也需要写在`unsafe`块中 19 | 20 | > **注意**:这里的例子使用了 x86/x86-64 汇编,不过所有平台都受支持。 21 | 22 | ## 汇编模板 23 | `assembly template`是唯一需要的参数并且必须是原始字符串(就是`""`) 24 | 25 | ```rust 26 | #![feature(asm)] 27 | 28 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 29 | fn foo() { 30 | unsafe { 31 | asm!("NOP"); 32 | } 33 | } 34 | 35 | // Other platforms: 36 | #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] 37 | fn foo() { /* ... */ } 38 | 39 | fn main() { 40 | // ... 41 | foo(); 42 | // ... 43 | } 44 | ``` 45 | 46 | (`feature(asm)`和`#[cfg]`从现在开始将被忽略。) 47 | 48 | 输出操作数,输入操作数,覆盖和选项都是可选的,然而如果你要省略它们的话,你必选加上正确数量的`:`: 49 | 50 | ```rust 51 | # #![feature(asm)] 52 | # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 53 | # fn main() { unsafe { 54 | asm!("xor %eax, %eax" 55 | : 56 | : 57 | : "eax" 58 | ); 59 | # } } 60 | # #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] 61 | # fn main() {} 62 | ``` 63 | 64 | 有空格在中间也没关系: 65 | 66 | ```rust 67 | # #![feature(asm)] 68 | # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 69 | # fn main() { unsafe { 70 | asm!("xor %eax, %eax" ::: "eax"); 71 | # } } 72 | # #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] 73 | # fn main() {} 74 | ``` 75 | 76 | ## 操作数 77 | 78 | 输入和输出操作数都有相同的格式:`: "constraints1"(expr1), "constraints2"(expr2), ..."`。输出操作数表达式必须是可变的左值,或还未赋值的: 79 | 80 | ```rust 81 | # #![feature(asm)] 82 | # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 83 | fn add(a: i32, b: i32) -> i32 { 84 | let c: i32; 85 | unsafe { 86 | asm!("add $2, $0" 87 | : "=r"(c) 88 | : "0"(a), "r"(b) 89 | ); 90 | } 91 | c 92 | } 93 | # #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] 94 | # fn add(a: i32, b: i32) -> i32 { a + b } 95 | 96 | fn main() { 97 | assert_eq!(add(3, 14159), 14162) 98 | } 99 | ``` 100 | 101 | 如果你想在这里使用真正的操作数,然而,要求你在你想使用的寄存器上套上大括号`{}`,并且要求你指明操作数的大小。这在非常底层的编程中是很有用的,这时你使用哪个寄存器是很重要的: 102 | 103 | ```rust 104 | # #![feature(asm)] 105 | # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 106 | # unsafe fn read_byte_in(port: u16) -> u8 { 107 | let result: u8; 108 | asm!("in %dx, %al" : "={al}"(result) : "{dx}"(port)); 109 | result 110 | # } 111 | ``` 112 | 113 | ## 覆盖(Clobbers) 114 | 115 | 一些指令修改的寄存器可能保存有不同的值,所以我们使用覆盖列表来告诉编译器不要假设任何装载在这些寄存器的值是有效的。 116 | 117 | ```rust 118 | # #![feature(asm)] 119 | # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 120 | # fn main() { unsafe { 121 | // Put the value 0x200 in eax: 122 | asm!("mov $$0x200, %eax" : /* no outputs */ : /* no inputs */ : "eax"); 123 | # } } 124 | # #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] 125 | # fn main() {} 126 | ``` 127 | 128 | 输入和输出寄存器并不需要列出因为这些信息已经通过给出的限制沟通过了。因此,任何其它的被使用的寄存器应该隐式或显式的被列出。 129 | 130 | 如果汇编修改了代码状态寄存器`cc`则需要在覆盖中被列出,如果汇编修改了内存,`memory`也应被指定。 131 | 132 | ## 选项(Options) 133 | 134 | 最后一部分,`options`是 Rust 特有的。格式是逗号分隔的基本字符串(也就是说,`:"foo", "bar", "baz"`)。它被用来指定关于内联汇编的额外信息: 135 | 136 | 目前有效的选项有: 137 | 138 | 1. *volatile* - 相当于 gcc/clang 中的`__asm__ __volatile__ (...)` 139 | 2. *alignstack* - 特定的指令需要栈按特定方式对齐(比如,SSE)并且指定这个告诉编译器插入通常的栈对齐代码 140 | 3. *intel* - 使用 intel 语法而不是默认的 AT&T 语法 141 | 142 | ```rust 143 | # #![feature(asm)] 144 | # #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 145 | # fn main() { 146 | let result: i32; 147 | unsafe { 148 | asm!("mov eax, 2" : "={eax}"(result) : : : "intel") 149 | } 150 | println!("eax is currently {}", result); 151 | # } 152 | # #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] 153 | # fn main() {} 154 | ``` 155 | 156 | ## 更多信息 157 | 目前`asm!`的实现是一个[LLVM内联汇编表达式](http://llvm.org/docs/LangRef.html#inline-assembler-expressions)的直接绑定,所以请确保充分的阅读[他们的文档](http://llvm.org/docs/LangRef.html#inline-assembler-expressions)来获取关于覆盖,限制等概念的更多信息。 158 | -------------------------------------------------------------------------------- /content/Casting Between Types 类型转换.md: -------------------------------------------------------------------------------- 1 | # 类型转换 2 | 3 | > [casting-between-types.md](https://github.com/rust-lang/book/blob/master/first-edition/src/casting-between-types.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | Rust,和它对安全的关注,提供了两种不同的在不同类型间转换的方式。第一个,`as`,用于安全转换。相反,`transmute`允许任意的转换,而这是 Rust 中最危险的功能之一! 8 | 9 | ## 强制转换(Coercion) 10 | 11 | 类型间的强制转换是隐式的并没有自己的语法,不过可以写作[`as`](#显式转换(explicit-coercions))。 12 | 13 | 强转出现在`let`,`const`和`static`语句;函数调用参数;结构体初始化的字符值;和函数返回值中。 14 | 15 | 最常用的强转的例子是从引用中去掉可变性: 16 | 17 | * `&mut T`到`&T` 18 | 19 | 一个相似的转换时去掉一个[裸指针](Raw Pointers 裸指针.md)的可变性: 20 | 21 | * `*mut T`到`*const T` 22 | 23 | 引用也能被强转为裸指针: 24 | 25 | * `&T`到`*const T` 26 | * `&mut T`到`*mut T` 27 | 28 | 自定义强转可以用[`Deref`](`Deref` coercions `Deref`强制多态.md)定义。 29 | 30 | 强转是可传递的。 31 | 32 | ## `as` 33 | 34 | `as`关键字进行安全的转换: 35 | 36 | ```rust 37 | let x: i32 = 5; 38 | 39 | let y = x as i64; 40 | ``` 41 | 42 | 有三种形式的安全转换:显式转换,数字类型之间的转换,和指针转换。 43 | 44 | 转换并不是可传递的:即便是`e as U1 as U2`是一个有效的表达式,`e as U2`也不必要是(事实上只有在`U1`强转为`U2`时才有效)。 45 | 46 | ### 显式转换(Explicit coercions) 47 | 48 | `e as U`是有效的仅当`e`是`T`类型而且`T`能强转为`U`。 49 | 50 | ### 数值转换 51 | 52 | `e as U`的转换在如下情况下也是有效的: 53 | 54 | * `e`是`T`类型而且`T`和`U`是任意数值类型:`numeric-cast` 55 | * `e`是一个类 C 语言枚举(变量并没有附加值),而且`U`是一个整型:`enum-cast` 56 | * `e`是`bool`或`char`而且`T`是一个整型:`prim-int-cast` 57 | * `e`是`u8`而且`U`是`char`:`u8-char-cast` 58 | 59 | 例如: 60 | 61 | ```rust 62 | let one = true as u8; 63 | let at_sign = 64 as char; 64 | let two_hundred = -56i8 as u8; 65 | ``` 66 | 67 | 数值转换的语义是: 68 | 69 | * 两个相同大小的整型之间(例如:`i32`->`u32`)的转换是一个`no-op` 70 | * 从一个大的整型转换为一个小的整型(例如:`u32`->`u8`)会截断 71 | * 从一个小的整型转换为一个大的整型(例如:`u8`->`u32`)会 72 | * 如果源类型是无符号的会补零(zero-extend) 73 | * 如果源类型是有符号的会符号(sign-extend) 74 | * 从一个浮点转换为一个整型会向 0 舍入 75 | * [注意:目前如果舍入的值并不能用目标整型表示的话会导致未定义行为(Undefined Behavior)](https://github.com/rust-lang/rust/issues/10184)。这包括 Inf 和 NaN。这是一个 bug 并会被修复。 76 | * 从一个整型转换为一个浮点会产生整型的浮点表示,如有必要会舍入(未指定舍入策略) 77 | * 从 f32 转换为 f64 是完美无缺的 78 | * 从 f64 转换为 f32 会产生最接近的可能值(未指定舍入策略) 79 | * [注意:目前如果值是有限的不过大于或小于 f32 所能表示的最大最小值会导致未定义行为(Undefined Behavior)](https://github.com/rust-lang/rust/issues/10184)。这是一个 bug 并会被修复。 80 | 81 | ### 指针转换 82 | 83 | 你也许会惊讶,[裸指针](Raw Pointers 裸指针.md)与整型之间的转换是安全的,而且不同类型的指针之间的转换遵循一些限制。只有解引用指针是不安全的: 84 | 85 | ```rust 86 | let a = 300 as *const char; // `a` is a pointer to location 300. 87 | let b = a as u32; 88 | ``` 89 | 90 | `e as U`在如下情况是一个有效的指针转换: 91 | 92 | * `e`是`*T`类型,`U`是`*U_0`类型,且要么`U_0: Sized`要么`unsize_kind(T) == unsize_kind(U_0)`:`ptr-ptr-cast` 93 | * `e`是`*T`类型且`U`是数值类型,同时`T: Sized`:`ptr-addr-cast` 94 | * `e`是一个整型且`U`是`*U_0`类型,同时`U_0: Sized`:`addr-ptr-cast` 95 | * `e`是`&[T; n]`类型且`U`是`*const T`类型:`array-ptr-cast` 96 | * `e`是函数指针且`U`是`*T`类型,同时`T: Sized`:`fptr-ptr-cast` 97 | * `e`是函数指针且`U`是一个整型:`fptr-addr-cast` 98 | 99 | 100 | ## `transmute` 101 | 102 | `as`只允许安全的转换,并会拒绝例如尝试将 4 个字节转换为一个`u32`: 103 | 104 | ```rust 105 | let a = [0u8, 0u8, 0u8, 0u8]; 106 | 107 | let b = a as u32; // Four u8s makes a u32. 108 | ``` 109 | 110 | 这个错误为: 111 | 112 | ```text 113 | error: non-scalar cast: `[u8; 4]` as `u32` 114 | let b = a as u32; // Four u8s makes a u32. 115 | ^~~~~~~~ 116 | ``` 117 | 118 | 这是一个“非标量转换(non-scalar cast)”因为这里我们有多个值:四个元素的数组。这种类型的转换是非常危险的,因为他们假设多种底层结构的实现方式。为此,我们需要一些更危险的东西。 119 | 120 | `transmute`函数由[编译器固有功能](Intrinsics 固有功能.md)提供,它做的工作非常简单,不过非常可怕。它告诉Rust对待一个类型的值就像它是另一个类型一样。它这样做并不管类型检查系统,并完全信任你。 121 | 122 | 在我们之前的例子中,我们知道一个有 4 个`u8`的数组可以正常代表一个`u32`,并且我们想要进行转换。使用`transmute`而不是`as`,Rust 允许我们: 123 | 124 | ```rust 125 | use std::mem; 126 | 127 | fn main() { 128 | unsafe { 129 | let a = [0u8, 1u8, 0u8, 0u8]; 130 | let b = mem::transmute::<[u8; 4], u32>(a); 131 | println!("{}", b); // 256 132 | // Or, more concisely: 133 | let c: u32 = mem::transmute(a); 134 | println!("{}", c); // 256 135 | } 136 | } 137 | ``` 138 | 139 | 为了使它编译通过我们要把这些操作封装到一个`unsafe`块中。技术上讲,只有`mem::transmute`调用自身需要位于块中,不过在这个情况下包含所有相关的内容是有好处的,这样你就知道该看哪了。在这例子中,`a`的细节也是重要的,所以它们放到了块中。你会看到各种风格的代码,有时上下文离得太远,因此在`unsafe`中包含所有的代码并不是一个好主意。 140 | 141 | 虽然`transmute`做了非常少的检查,至少它确保了这些类型是相同大小的,这个错误: 142 | 143 | ```rust 144 | use std::mem; 145 | 146 | unsafe { 147 | let a = [0u8, 0u8, 0u8, 0u8]; 148 | 149 | let b = mem::transmute::<[u8; 4], u64>(a); 150 | } 151 | ``` 152 | 153 | 和: 154 | 155 | ```text 156 | error: transmute called with differently sized types: [u8; 4] (32 bits) to u64 157 | (64 bits) 158 | ``` 159 | 160 | 除了这些,你可以自行随意转换,只能帮你这么多了! 161 | -------------------------------------------------------------------------------- /content/Associated Types 关联类型.md: -------------------------------------------------------------------------------- 1 | # 关联类型 2 | 3 | > [associated-types.md](https://github.com/rust-lang/book/blob/master/first-edition/src/associated-types.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | 关联类型是 Rust 类型系统中非常强大的一部分。它涉及到‘类型族’的概念,换句话说,就是把多种类型归于一类。这个描述可能比较抽象,所以让我们深入研究一个例子。如果你想编写一个`Graph` trait,你需要泛型化两个类型:点类型和边类型。所以你可能会像这样写一个 trait,`Graph`: 8 | 9 | ```rust 10 | trait Graph { 11 | fn has_edge(&self, &N, &N) -> bool; 12 | fn edges(&self, &N) -> Vec; 13 | // Etc. 14 | } 15 | ``` 16 | 17 | 虽然这可以工作,不过显得很尴尬,例如,任何需要一个`Graph`作为参数的函数都需要泛型化的`N`ode和`E`dge类型: 18 | 19 | ```rust 20 | fn distance>(graph: &G, start: &N, end: &N) -> u32 { ... } 21 | ``` 22 | 23 | 我们的距离计算并不需要`Edge`类型,所以函数签名中`E`只是写着玩的。 24 | 25 | 我们需要的是对于每一种`Graph`类型,都使用一个特定的的`N`ode和`E`dge类型。我们可以用关联类型来做到这一点: 26 | 27 | ```rust 28 | trait Graph { 29 | type N; 30 | type E; 31 | 32 | fn has_edge(&self, &Self::N, &Self::N) -> bool; 33 | fn edges(&self, &Self::N) -> Vec; 34 | // Etc. 35 | } 36 | ``` 37 | 38 | 现在,我们使用一个抽象的`Graph`了: 39 | 40 | ```rust 41 | fn distance(graph: &G, start: &G::N, end: &G::N) -> uint { ... } 42 | ``` 43 | 44 | 这里不再需要处理`E`dge类型了。 45 | 46 | 让我们更详细的回顾一下。 47 | 48 | ## 定义关联类型 49 | 50 | 让我们构建一个`Graph`trait。这里是定义: 51 | 52 | ```rust 53 | trait Graph { 54 | type N; 55 | type E; 56 | 57 | fn has_edge(&self, &Self::N, &Self::N) -> bool; 58 | fn edges(&self, &Self::N) -> Vec; 59 | } 60 | ``` 61 | 62 | 十分简单。关联类型使用`type`关键字,并出现在trait体和函数中。 63 | 64 | 这些`type`声明跟函数定义一样。例如,如果我们想`N`类型实现`Display`,这样我们就可以打印出点类型,我们可以这样写: 65 | 66 | ```rust 67 | use std::fmt; 68 | 69 | trait Graph { 70 | type N: fmt::Display; 71 | type E; 72 | 73 | fn has_edge(&self, &Self::N, &Self::N) -> bool; 74 | fn edges(&self, &Self::N) -> Vec; 75 | } 76 | ``` 77 | 78 | ## 实现关联类型 79 | 80 | 就像任何 trait,使用关联类型的 trait 用`impl`关键字来提供实现。下面是一个`Graph`的简单实现: 81 | 82 | ```rust 83 | # trait Graph { 84 | # type N; 85 | # type E; 86 | # fn has_edge(&self, &Self::N, &Self::N) -> bool; 87 | # fn edges(&self, &Self::N) -> Vec; 88 | # } 89 | struct Node; 90 | 91 | struct Edge; 92 | 93 | struct MyGraph; 94 | 95 | impl Graph for MyGraph { 96 | type N = Node; 97 | type E = Edge; 98 | 99 | fn has_edge(&self, n1: &Node, n2: &Node) -> bool { 100 | true 101 | } 102 | 103 | fn edges(&self, n: &Node) -> Vec { 104 | Vec::new() 105 | } 106 | } 107 | ``` 108 | 109 | 这个可笑的实现总是返回`true`和一个空的`Vec`,不过它提供了如何实现这类 trait 的思路。首先我们需要3个`struct`,一个代表图,一个代表点,还有一个代表边。如果使用别的类型更合理,也可以那样做,我们只是准备使用`struct`来代表这 3 个类型。 110 | 111 | 接下来是`impl`行,它就像其它任何 trait 的实现。 112 | 113 | 在这里,我们使用`=`来定义我们的关联类型。trait 使用的名字出现在`=`的左边,而我们`impl`的具体类型出现在右边。最后,我们在函数声明中使用具体类型。 114 | 115 | ## trait 对象和关联类型 116 | 117 | 这里还有另外一个我们需要讨论的语法:trait对象。如果你试图从一个带有关联类型的 trait 创建一个 trait 对象,像这样: 118 | 119 | ```rust 120 | # trait Graph { 121 | # type N; 122 | # type E; 123 | # fn has_edge(&self, &Self::N, &Self::N) -> bool; 124 | # fn edges(&self, &Self::N) -> Vec; 125 | # } 126 | # struct Node; 127 | # struct Edge; 128 | # struct MyGraph; 129 | # impl Graph for MyGraph { 130 | # type N = Node; 131 | # type E = Edge; 132 | # fn has_edge(&self, n1: &Node, n2: &Node) -> bool { 133 | # true 134 | # } 135 | # fn edges(&self, n: &Node) -> Vec { 136 | # Vec::new() 137 | # } 138 | # } 139 | let graph = MyGraph; 140 | let obj = Box::new(graph) as Box; 141 | ``` 142 | 143 | 你会得到两个错误: 144 | 145 | ```text 146 | error: the value of the associated type `E` (from the trait `main::Graph`) must 147 | be specified [E0191] 148 | let obj = Box::new(graph) as Box; 149 | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 150 | 24:44 error: the value of the associated type `N` (from the trait 151 | `main::Graph`) must be specified [E0191] 152 | let obj = Box::new(graph) as Box; 153 | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 154 | ``` 155 | 156 | 我们不能这样创建一个trait对象,因为我们并不知道关联的类型。相反,我们可以这样写: 157 | 158 | ```rust 159 | # trait Graph { 160 | # type N; 161 | # type E; 162 | # fn has_edge(&self, &Self::N, &Self::N) -> bool; 163 | # fn edges(&self, &Self::N) -> Vec; 164 | # } 165 | # struct Node; 166 | # struct Edge; 167 | # struct MyGraph; 168 | # impl Graph for MyGraph { 169 | # type N = Node; 170 | # type E = Edge; 171 | # fn has_edge(&self, n1: &Node, n2: &Node) -> bool { 172 | # true 173 | # } 174 | # fn edges(&self, n: &Node) -> Vec { 175 | # Vec::new() 176 | # } 177 | # } 178 | let graph = MyGraph; 179 | let obj = Box::new(graph) as Box>; 180 | ``` 181 | 182 | `N=Node`语法允许我们提供一个具体类型,`Node`,作为`N`类型参数。`E=Edge`也是一样。如果我们不提供这个限制,我们不能确定应该`impl`那个来匹配trait对象。 183 | -------------------------------------------------------------------------------- /content/Structs 结构体.md: -------------------------------------------------------------------------------- 1 | # 结构体 2 | 3 | > [structs.md](https://github.com/rust-lang/book/blob/master/first-edition/src/structs.md) 4 | >
5 | > commit 59e5e65270259666422e51721cc42f261f827386 6 | 7 | 结构体是一个创建更复杂数据类型的方法。例如,如果我们正在进行涉及到 2D 空间坐标的计算,我们将需要一个`x`和一个`y`值: 8 | 9 | ```rust 10 | let origin_x = 0; 11 | let origin_y = 0; 12 | ``` 13 | 14 | 结构体让我们组合它们俩为一个单独,统一的数据类型: 15 | 16 | ```rust 17 | struct Point { 18 | x: i32, 19 | y: i32, 20 | } 21 | 22 | fn main() { 23 | let origin = Point { x: 0, y: 0 }; // origin: Point 24 | 25 | println!("The origin is at ({}, {})", origin.x, origin.y); 26 | } 27 | ``` 28 | 29 | 这里有许多细节,让我们分开说。我们使用了`struct`关键字后跟名字来定义了一个结构体。根据传统,结构体使用大写字母开头并且使用驼峰命名法:`PointInSpace`而不要写成`Point_In_Space`。 30 | 31 | 像往常一样我们用`let`创建了一个结构体的实例,不过我们用`key: value`语法设置了每个字段。这里顺序不必和声明的时候一致。 32 | 33 | 最后,因为每个字段都有名字,我们可以访问字段通过圆点记法:`origin.x`。 34 | 35 | 结构体中的值默认是不可变的,就像 Rust 中其它的绑定一样。使用`mut`使其可变: 36 | 37 | ```rust 38 | struct Point { 39 | x: i32, 40 | y: i32, 41 | } 42 | 43 | fn main() { 44 | let mut point = Point { x: 0, y: 0 }; 45 | 46 | point.x = 5; 47 | 48 | println!("The point is at ({}, {})", point.x, point.y); 49 | } 50 | ``` 51 | 52 | 上面的代码会打印`The point is at (5, 0)`。 53 | 54 | Rust 在语言级别不支持字段可变性,所以你不能像这么写: 55 | 56 | ```rust,ignore 57 | struct Point { 58 | mut x: i32, // This causes an error. 59 | y: i32, 60 | } 61 | ``` 62 | 63 | 可变性是绑定的一个属性,不是结构体自身的。如果你习惯于字段级别的可变性,这开始可能看起来有点奇怪,不过这样明显地简化了问题。它甚至可以让你使变量只可变一段临时时间: 64 | 65 | ```rust 66 | struct Point { 67 | x: i32, 68 | y: i32, 69 | } 70 | 71 | fn main() { 72 | let mut point = Point { x: 0, y: 0 }; 73 | 74 | point.x = 5; 75 | 76 | let point = point; // `point` is now immutable. 77 | 78 | point.y = 6; // This causes an error. 79 | } 80 | ``` 81 | 82 | 你的结构体仍然可以包含`&mut`指针,它会给你一些类型的可变性: 83 | 84 | ```rust 85 | struct Point { 86 | x: i32, 87 | y: i32, 88 | } 89 | 90 | struct PointRef<'a> { 91 | x: &'a mut i32, 92 | y: &'a mut i32, 93 | } 94 | 95 | fn main() { 96 | let mut point = Point { x: 0, y: 0 }; 97 | 98 | { 99 | let r = PointRef { x: &mut point.x, y: &mut point.y }; 100 | 101 | *r.x = 5; 102 | *r.y = 6; 103 | } 104 | 105 | assert_eq!(5, point.x); 106 | assert_eq!(6, point.y); 107 | } 108 | ``` 109 | 110 | ## 更新语法(Update syntax) 111 | 112 | 一个包含`..`的`struct`表明你想要使用一些其它结构体的拷贝的一些值。例如: 113 | 114 | ```rust 115 | struct Point3d { 116 | x: i32, 117 | y: i32, 118 | z: i32, 119 | } 120 | 121 | let mut point = Point3d { x: 0, y: 0, z: 0 }; 122 | point = Point3d { y: 1, .. point }; 123 | ``` 124 | 125 | 这给了`point`一个新的`y`,不过保留了`x`和`z`的值。这也并不必要是同样的`struct`,你可以在创建新结构体时使用这个语法,并会拷贝你未指定的值: 126 | 127 | ```rust 128 | # struct Point3d { 129 | # x: i32, 130 | # y: i32, 131 | # z: i32, 132 | # } 133 | let origin = Point3d { x: 0, y: 0, z: 0 }; 134 | let point = Point3d { z: 1, x: 2, .. origin }; 135 | ``` 136 | 137 | ## 元组结构体 138 | 139 | Rust 有像另一个[元组](Primitive Types 原生类型.md#tuples)和结构体的混合体的数据类型。元组结构体有一个名字,不过它的字段没有。他们用`struct`关键字声明,并元组前面带有一个名字: 140 | 141 | ```rust 142 | struct Color(i32, i32, i32); 143 | struct Point(i32, i32, i32); 144 | 145 | let black = Color(0, 0, 0); 146 | let origin = Point(0, 0, 0); 147 | ``` 148 | 149 | 这里`black`和`origin`并不是相同的类型,即使它们有一模一样的值。 150 | 151 | 元组结构体结构体的成员可以使用点标记或者解构`let`访问,就像常规的元组: 152 | 153 | ```rust 154 | # struct Color(i32, i32, i32); 155 | # struct Point(i32, i32, i32); 156 | # let black = Color(0, 0, 0); 157 | # let origin = Point(0, 0, 0); 158 | let black_r = black.0; 159 | let Point(_, origin_y, origin_z) = origin; 160 | ``` 161 | 162 | 像`Point(_, origin_y, origin_z)`这样的模式也可以用于[match 表达式](Match 匹配.md)。 163 | 164 | 一个元组结构体非常有用的情况是当他只有一个元素时,我们称之为“新类型(newtype)”模式,因为它允许创建一个区别于它包含的值的类型,同时也标明它的语义: 165 | 166 | ```rust 167 | struct Inches(i32); 168 | 169 | let length = Inches(10); 170 | 171 | let Inches(integer_length) = length; 172 | println!("length is {} inches", integer_length); 173 | ``` 174 | 175 | 如上所示,通过解构`let`可以获取其中的整型值。在这里,`let Inches(integer_length)`将`10`赋值于`integer_length`。我们可以用点标记做到同样的事: 176 | 177 | ```rust 178 | # struct Inches(i32); 179 | # let length = Inches(10); 180 | let integer_length = length.0; 181 | ``` 182 | 183 | 几乎总是可以在使用元组结构体的地方使用`struct`,并可能更明确一些。我们可以这样重写`Color`和`Point`: 184 | 185 | ```rust 186 | struct Color { 187 | red: i32, 188 | blue: i32, 189 | green: i32, 190 | } 191 | 192 | struct Point { 193 | x: i32, 194 | y: i32, 195 | z: i32, 196 | } 197 | ``` 198 | 199 | 好的名字是很重要的,同时元组结构体中的值也可以使用点语法被引用,`struct`提供了真实的名字,而不仅仅是位置。 200 | 201 | ## 类单元结构体(Unit-like structs) 202 | 203 | 你可以定义一个没有任何成员的结构体: 204 | 205 | ```rust 206 | struct Electron {} // Use empty braces... 207 | struct Proton; // ...or just a semicolon. 208 | 209 | // Use the same notation when creating an instance. 210 | let x = Electron {}; 211 | let y = Proton; 212 | let z = Electron; // Error 213 | ``` 214 | 215 | 这样的结构体叫做“类单元”因为它与一个空元组类似,`()`,这有时叫做“单元”。就像一个元组结构体,它定义了一个新类型。 216 | 217 | 就它本身来看没什么用(虽然有时它可以作为一个标记类型),不过在与其它功能的结合中,它可以变得有用。例如,一个库可能请求你创建一个实现了一个特定特性的结构来处理事件。如果你并不需要在结构中存储任何数据,你可以仅仅创建一个类单元结构体。 218 | -------------------------------------------------------------------------------- /content/Custom Allocators 自定义内存分配器.md: -------------------------------------------------------------------------------- 1 | # 自定义内存分配器 2 | 3 | > [custom-allocators.md](https://github.com/rust-lang/rust/blob/stable/src/doc/book/custom-allocators.md) 4 | >
5 | > commit 28548db57d0acbc00ee80b43816953dbe31d53ba 6 | 7 | 分配内存并不总是最简单的事情,同时通常 Rust 默认会负责它,不过经常自定义内存分配会变得必要。编译器和标准库目前允许在编译时切换目前默认使用的全局分配器。设计目前称作[RFC 1183](https://github.com/rust-lang/rfcs/blob/master/text/1183-swap-out-jemalloc.md)不过这里我们会教你如何获取你自己的分配器并运行起来。 8 | 9 | ## 默认分配器 10 | 11 | 编译器目前自带两个默认分配器:`alloc_system`和`alloc_jemalloc`(然而一些目标平台并没有 jemalloc)。这些分配器是正常的 Rust crate 并包含分配和释放内存的 routine 的实现。标准库并不假设使用任何一个编译,而且编译器会在编译时根据被产生的输出类型决定使用哪个分配器。 12 | 13 | 编译器产生的二进制文件默认会使用`alloc_jemalloc`(如果可用的话)。在这种情况下编译器“控制了一切”,从它超过了最终链接的权利的角度来看。大体上这意味着分配器选择可以被交给编译器。 14 | 15 | 动态和静态库,然而,默认使用`alloc_system`。这里 Rust 通常是其他程序的“客人”或者处于并没有权决定应使用的分配器的世界。为此它求助于标准 API(例如,`malloc`和`free`)来获取和释放内存。 16 | 17 | ## 切换分配器 18 | 19 | 虽然编译器默认的选择大部分情况工作良好,也经常需要定制特定的方面。覆盖编译器关于使用哪个分配器的选择可以简单的通过链接到期望的分配器实现: 20 | 21 | ```rust 22 | #![feature(alloc_system)] 23 | 24 | extern crate alloc_system; 25 | 26 | fn main() { 27 | let a = Box::new(4); // Allocates from the system allocator. 28 | println!("{}", a); 29 | } 30 | ``` 31 | 32 | 在这个例子中生成的二进制文件并不会默认链接到 jemalloc 而是使用了系统分配器。同理生成一个默认使用 jemalloc 的动态库可以写成: 33 | 34 | ```rust 35 | #![feature(alloc_jemalloc)] 36 | #![crate_type = "dylib"] 37 | 38 | extern crate alloc_jemalloc; 39 | 40 | pub fn foo() { 41 | let a = Box::new(4); // Allocates from jemalloc. 42 | println!("{}", a); 43 | } 44 | # fn main() {} 45 | ``` 46 | 47 | ### 编写一个自定义分配器 48 | 49 | 有时甚至 jemalloc 与系统分配器之间的选择都是不够的并需要一个新的自定义的分配器。这种情况你要编写你自己实现了分配器 API(例如与`alloc_system`和`alloc_jemallo`相同)的 crate。作为一个例子,让我们看看一个简单的和声明化的`alloc_system`版本: 50 | 51 | ```rust 52 | # // Only needed for rustdoc --test down below. 53 | # #![feature(lang_items)] 54 | // The compiler needs to be instructed that this crate is an allocator in order 55 | // to realize that when this is linked in another allocator like jemalloc should 56 | // not be linked in. 57 | #![feature(allocator)] 58 | #![allocator] 59 | 60 | // Allocators are not allowed to depend on the standard library which in turn 61 | // requires an allocator in order to avoid circular dependencies. This crate, 62 | // however, can use all of libcore. 63 | #![no_std] 64 | 65 | // Let's give a unique name to our custom allocator: 66 | #![crate_name = "my_allocator"] 67 | #![crate_type = "rlib"] 68 | 69 | // Our system allocator will use the in-tree libc crate for FFI bindings. Note 70 | // that currently the external (crates.io) libc cannot be used because it links 71 | // to the standard library (e.g. `#![no_std]` isn't stable yet), so that's why 72 | // this specifically requires the in-tree version. 73 | #![feature(libc)] 74 | extern crate libc; 75 | 76 | // Listed below are the five allocation functions currently required by custom 77 | // allocators. Their signatures and symbol names are not currently typechecked 78 | // by the compiler, but this is a future extension and are required to match 79 | // what is found below. 80 | // 81 | // Note that the standard `malloc` and `realloc` functions do not provide a way 82 | // to communicate alignment so this implementation would need to be improved 83 | // with respect to alignment in that aspect. 84 | 85 | #[no_mangle] 86 | pub extern fn __rust_allocate(size: usize, _align: usize) -> *mut u8 { 87 | unsafe { libc::malloc(size as libc::size_t) as *mut u8 } 88 | } 89 | 90 | #[no_mangle] 91 | pub extern fn __rust_deallocate(ptr: *mut u8, _old_size: usize, _align: usize) { 92 | unsafe { libc::free(ptr as *mut libc::c_void) } 93 | } 94 | 95 | #[no_mangle] 96 | pub extern fn __rust_reallocate(ptr: *mut u8, _old_size: usize, size: usize, 97 | _align: usize) -> *mut u8 { 98 | unsafe { 99 | libc::realloc(ptr as *mut libc::c_void, size as libc::size_t) as *mut u8 100 | } 101 | } 102 | 103 | #[no_mangle] 104 | pub extern fn __rust_reallocate_inplace(_ptr: *mut u8, old_size: usize, 105 | _size: usize, _align: usize) -> usize { 106 | old_size // This api is not supported by libc. 107 | } 108 | 109 | #[no_mangle] 110 | pub extern fn __rust_usable_size(size: usize, _align: usize) -> usize { 111 | size 112 | } 113 | 114 | # // Only needed to get rustdoc to test this: 115 | # fn main() {} 116 | # #[lang = "panic_fmt"] fn panic_fmt() {} 117 | # #[lang = "eh_personality"] fn eh_personality() {} 118 | # #[lang = "eh_unwind_resume"] extern fn eh_unwind_resume() {} 119 | # #[no_mangle] pub extern fn rust_eh_register_frames () {} 120 | # #[no_mangle] pub extern fn rust_eh_unregister_frames () {} 121 | ``` 122 | 123 | 在我们编译了这个 crate 之后,他可以被如下使用: 124 | 125 | ```rust 126 | extern crate my_allocator; 127 | 128 | fn main() { 129 | let a = Box::new(8); // Allocates memory via our custom allocator crate. 130 | println!("{}", a); 131 | } 132 | ``` 133 | 134 | ### 自定义分配器的限制 135 | 136 | 使用自定义分配器时要满足一些限制,否则可能导致编译器错误: 137 | 138 | * 任何一个程序只能链接到一个分配器。二进制、动态库和静态库必须正好链接到一个分配器上,并且,如果没有显式选择,编译器会选择一个。另一方面,rlib 并不需要链接到一个分配器(不过仍然可以)。 139 | 140 | * 一个标记为`#![needs_allocator]`(例如,目前的`liballoc`)的分配器使用者和一个`#[allocator]` crate 不能直接依赖一个需要分配器的 crate(例如,循环引用是不允许的)。这基本上意味着目前分配器必须依赖 libcore。 141 | -------------------------------------------------------------------------------- /content/Method Syntax 方法语法.md: -------------------------------------------------------------------------------- 1 | # 方法语法 2 | 3 | > [method-syntax.md](https://github.com/rust-lang/book/blob/master/first-edition/src/method-syntax.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | 函数是伟大的,不过如果你在一些数据上调用了一堆函数,这将是令人尴尬的。 8 | 考虑下面代码: 9 | 10 | ```rust 11 | baz(bar(foo)); 12 | ``` 13 | 14 | 我们可以从左向右阅读,我们会看到“baz bar foo”。不过这不是函数被调用的顺序,调用应该是从内向外的:“foo bar baz”。如果能这么做不是更好吗? 15 | 16 | ```rust 17 | foo.bar().baz(); 18 | ``` 19 | 20 | 幸运的是,正如对上面那个问题的猜测,你可以!Rust 通过`impl`关键字提供了使用**方法调用语法**(*method call syntax*)。 21 | 22 | ## 方法调用 23 | 24 | 这是它如何工作的: 25 | 26 | ```rust 27 | struct Circle { 28 | x: f64, 29 | y: f64, 30 | radius: f64, 31 | } 32 | 33 | impl Circle { 34 | fn area(&self) -> f64 { 35 | std::f64::consts::PI * (self.radius * self.radius) 36 | } 37 | } 38 | 39 | fn main() { 40 | let c = Circle { x: 0.0, y: 0.0, radius: 2.0 }; 41 | println!("{}", c.area()); 42 | } 43 | ``` 44 | 45 | 这会打印`12.566371`。 46 | 47 | 我们创建了一个代表圆的结构体。我们写了一个`impl`块,并且在里面定义了一个方法,`area`。 48 | 49 | 方法的第一参数比较特殊,`&self`。它有3种变体:`self`,`&self`和`&mut self`。你可以认为这第一个参数就是`x.foo()`中的`x`。这3种变体对应`x`可能的3种类型:`self`如果它只是栈上的一个值,`&self`如果它是一个引用,然后`&mut self`如果它是一个可变引用。因为我们的`area`以`&self`作为参数,我们就可以可以像其他参数那样使用它。因为我们知道是一个`Circle`,我们可以像任何其他结构体那样访问`radius`字段。 50 | 51 | 我们应该默认使用`&self`,就像相比获取所有权你应该更倾向于借用,同样相比获取可变引用更倾向于不可变引用一样。这是一个三种变体的例子: 52 | 53 | ```rust 54 | struct Circle { 55 | x: f64, 56 | y: f64, 57 | radius: f64, 58 | } 59 | 60 | impl Circle { 61 | fn reference(&self) { 62 | println!("taking self by reference!"); 63 | } 64 | 65 | fn mutable_reference(&mut self) { 66 | println!("taking self by mutable reference!"); 67 | } 68 | 69 | fn takes_ownership(self) { 70 | println!("taking ownership of self!"); 71 | } 72 | } 73 | ``` 74 | 75 | 你可以有任意多个`impl`块。上面的例子也可以被写成这样: 76 | 77 | ```rust 78 | struct Circle { 79 | x: f64, 80 | y: f64, 81 | radius: f64, 82 | } 83 | 84 | impl Circle { 85 | fn reference(&self) { 86 | println!("taking self by reference!"); 87 | } 88 | } 89 | 90 | impl Circle { 91 | fn mutable_reference(&mut self) { 92 | println!("taking self by mutable reference!"); 93 | } 94 | } 95 | 96 | impl Circle { 97 | fn takes_ownership(self) { 98 | println!("taking ownership of self!"); 99 | } 100 | } 101 | ``` 102 | 103 | ## 链式方法调用(Chaining method calls) 104 | 105 | 现在我们知道如何调用方法了,例如`foo.bar()`。那么我们最开始的那个例子呢,`foo.bar().baz()`?我们称这个为“方法链”,我们可以通过返回`self`来做到这点。 106 | 107 | ```rust 108 | struct Circle { 109 | x: f64, 110 | y: f64, 111 | radius: f64, 112 | } 113 | 114 | impl Circle { 115 | fn area(&self) -> f64 { 116 | std::f64::consts::PI * (self.radius * self.radius) 117 | } 118 | 119 | fn grow(&self, increment: f64) -> Circle { 120 | Circle { x: self.x, y: self.y, radius: self.radius + increment } 121 | } 122 | } 123 | 124 | fn main() { 125 | let c = Circle { x: 0.0, y: 0.0, radius: 2.0 }; 126 | println!("{}", c.area()); 127 | 128 | let d = c.grow(2.0).area(); 129 | println!("{}", d); 130 | } 131 | ``` 132 | 133 | 注意返回值: 134 | 135 | ```rust 136 | # struct Circle; 137 | # impl Circle { 138 | fn grow(&self, increment: f64) -> Circle { 139 | # Circle } } 140 | ``` 141 | 142 | 我们看到我们返回了一个`Circle`。通过这个函数,我们可以增长一个圆的面积到任意大小。 143 | 144 | ## 关联函数(Associated functions) 145 | 146 | 我们也可以定义一个不带`self`参数的关联函数。这是一个 Rust 代码中非常常见的模式: 147 | 148 | ```rust 149 | struct Circle { 150 | x: f64, 151 | y: f64, 152 | radius: f64, 153 | } 154 | 155 | impl Circle { 156 | fn new(x: f64, y: f64, radius: f64) -> Circle { 157 | Circle { 158 | x: x, 159 | y: y, 160 | radius: radius, 161 | } 162 | } 163 | } 164 | 165 | fn main() { 166 | let c = Circle::new(0.0, 0.0, 2.0); 167 | } 168 | ``` 169 | 170 | 这个**关联函数**(*associated function*)为我们构建了一个新的`Circle`。注意静态函数是通过`Struct::method()`语法调用的,而不是`ref.method()`语法。 171 | 172 | ## 创建者模式(Builder Pattern) 173 | 174 | 我们说我们需要我们的用户可以创建圆,不过我们只允许他们设置他们关心的属性。否则,`x`和`y`将是`0.0`,并且`radius`将是`1.0`。Rust 并没有方法重载,命名参数或者可变参数。我们利用创建者模式来代替。它看起像这样: 175 | 176 | ```rust 177 | struct Circle { 178 | x: f64, 179 | y: f64, 180 | radius: f64, 181 | } 182 | 183 | impl Circle { 184 | fn area(&self) -> f64 { 185 | std::f64::consts::PI * (self.radius * self.radius) 186 | } 187 | } 188 | 189 | struct CircleBuilder { 190 | x: f64, 191 | y: f64, 192 | radius: f64, 193 | } 194 | 195 | impl CircleBuilder { 196 | fn new() -> CircleBuilder { 197 | CircleBuilder { x: 0.0, y: 0.0, radius: 1.0, } 198 | } 199 | 200 | fn x(&mut self, coordinate: f64) -> &mut CircleBuilder { 201 | self.x = coordinate; 202 | self 203 | } 204 | 205 | fn y(&mut self, coordinate: f64) -> &mut CircleBuilder { 206 | self.y = coordinate; 207 | self 208 | } 209 | 210 | fn radius(&mut self, radius: f64) -> &mut CircleBuilder { 211 | self.radius = radius; 212 | self 213 | } 214 | 215 | fn finalize(&self) -> Circle { 216 | Circle { x: self.x, y: self.y, radius: self.radius } 217 | } 218 | } 219 | 220 | fn main() { 221 | let c = CircleBuilder::new() 222 | .x(1.0) 223 | .y(2.0) 224 | .radius(2.0) 225 | .finalize(); 226 | 227 | println!("area: {}", c.area()); 228 | println!("x: {}", c.x); 229 | println!("y: {}", c.y); 230 | } 231 | ``` 232 | 233 | 我们在这里又声明了一个结构体,`CircleBuilder`。我们给它定义了一个创建者函数。我们也在`Circle`中定义了`area()`方法。我们还定义了另一个方法`CircleBuilder: finalize()`。这个方法从构造器中创建了我们最后的`Circle`。现在我们使用类型系统来强化我们的考虑:我们可以用`CircleBuilder`来强制生成我们需要的`Circle`。 234 | -------------------------------------------------------------------------------- /content/Ownership 所有权.md: -------------------------------------------------------------------------------- 1 | # 所有权 2 | 3 | > [ownership.md](https://github.com/rust-lang/book/blob/master/first-edition/src/ownership.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | 这篇教程是现行 3 个 Rust 所有权系统章节的第一部分。所有权系统是Rust最独特且最引人入胜的特性之一,也是作为 Rust 开发者应该熟悉的。Rust 所追求最大的目标 -- 内存安全,关键在于所有权。所有权系统有一些不同的概念,每个概念独自成章: 8 | 9 | * 所有权,你正在阅读的这个章节 10 | * [借用](5.9.References and Borrowing 引用和借用.md),以及它关联的特性: "引用" (references) 11 | * [生命周期](5.10.Lifetimes 生命周期.md),关于借用的高级概念 12 | 13 | 这3章依次互相关联,你需要完整地阅读全部 3 章来对Rust的所有权系统进行全面的了解。 14 | 15 | ## 原则(Meta) 16 | 17 | 在我们开始详细讲解之前,这有两点关于所有权系统重要的注意事项。 18 | 19 | Rust 注重安全和速度。它通过很多**零开销抽象**(*zero-cost abstractions*)来实现这些目标,也就是说在 Rust 中,实现抽象的开销尽可能的小。所有权系统是一个典型的零开销抽象的例子。本文提到所有的分析都是**在编译时完成的**。你不需要在运行时为这些功能付出任何开销。 20 | 21 | 然而,这个系统确实有一个开销:学习曲线。很多 Rust 初学者会经历我们所谓的“与借用检查器作斗争”的过程,也就是指 Rust 编译器拒绝编译一个作者认为合理的程序。这种“斗争”会因为程序员关于所有权系统如何工作的基本模型与 Rust 实现的实际规则不匹配而经常发生。当你刚开始尝试 Rust 的时候,你很可能会有相似的经历。然而有一个好消息:更有经验的 Rust 开发者反映,一旦他们适应所有权系统一段时间之后,与借用检查器的冲突会越来越少。 22 | 23 | 记住这些之后,让我们来学习关于所有权的内容。 24 | 25 | ## 所有权(Ownership) 26 | 27 | Rust 中的[变量绑定](Variable Bindings 变量绑定.md)有一个属性:它们有它们所绑定的的值的**所有权**。这意味着当一个绑定离开作用域,它们绑定的资源就会被释放。例如: 28 | 29 | ```rust 30 | fn foo() { 31 | let v = vec![1, 2, 3]; 32 | } 33 | ``` 34 | 35 | 当`v`进入作用域,一个新的 vector 在栈上被创建,并在[堆](The Stack and the Heap 栈和堆.md)上为它的3个元素分配了空间。当`v`在`foo()`的末尾离开作用域,Rust将会清理掉与向量(vector)相关的一切,甚至是堆上分配的内存。这在作用域的结尾是一定(deterministically)会发生的。 36 | 37 | 我们在之前的章节介绍了[vector](Vectors.md);这里我们只是用它来作为一个在运行时在堆上分配内存的类型的例子。他们表现起来像[数组](Primitive Types 原生类型.md#数组),除了通过`push()`更多元素他们的大小会改变。 38 | 39 | vector 有一个[泛型类型](Generics 泛型.md)`Vec`,所以在这个例子中`v`是`Vec`类型的。我们将会在本章的后面详细介绍泛型。 40 | 41 | ## 移动语义(Move semantics) 42 | 43 | 然而这里有更巧妙的地方:Rust 确保了对于任何给定的资源都**正好(只)有一个**绑定与之对应。例如,如果我们有一个 vector,我们可以把它赋予另外一个绑定: 44 | 45 | ```rust 46 | let v = vec![1, 2, 3]; 47 | 48 | let v2 = v; 49 | ``` 50 | 51 | 不过,如果之后我们尝试使用`v`,我们得到一个错误: 52 | 53 | ```rust 54 | let v = vec![1, 2, 3]; 55 | 56 | let v2 = v; 57 | 58 | println!("v[0] is: {}", v[0]); 59 | ``` 60 | 61 | 它看起来像这样: 62 | 63 | ```text 64 | error: use of moved value: `v` 65 | println!("v[0] is: {}", v[0]); 66 | ^ 67 | ``` 68 | 69 | 当我们定义了一个取得所有权的函数,并尝试在我们把变量作为参数传递给函数之后使用这个变量时,会发生相似的事情: 70 | 71 | ```rust 72 | fn take(v: Vec) { 73 | // What happens here isn’t important. 74 | } 75 | 76 | let v = vec![1, 2, 3]; 77 | 78 | take(v); 79 | 80 | println!("v[0] is: {}", v[0]); 81 | ``` 82 | 83 | 一样的错误:“use of moved value”。当我们把所有权转移给别的绑定时,我们说我们“移动”了我们引用的值。这里你并不需要什么类型的特殊注解,这是 Rust 的默认行为。 84 | 85 | ## 细节 86 | 87 | 在移动了绑定后我们不能使用它的原因是微妙的,也是重要的。当我们写了这样的代码: 88 | 89 | ```rust 90 | let x = 10; 91 | ``` 92 | 93 | Rust 在[栈](The Stack and the Heap 栈和堆.md)上为一个整型(`i32`)分配了内存,将代表值 10 的位拷贝到分配的内存中并将这个内存区域绑定到变量 x 上以供进一步的引用。 94 | 95 | 现在考虑一下下面的代码段: 96 | 97 | ```rust 98 | let v = vec![1, 2, 3]; 99 | 100 | let mut v2 = v; 101 | ``` 102 | 103 | 第一行在栈上为 vector 对象`v`分配了内存,就像上面的 x 一样。不过同时它也在[堆](The Stack and the Heap 栈和堆.md)上为实际的数据 [1, 2, 3] 分配了一些内存。Rust 拷贝堆上分配的内存的地址到一个内部指针,作为位于栈上的 vector 对象的一部分(让我们叫它数据指针)。值得指出的是(即便冒着冗余的风险),我们也将 vector 对象和它的数据存储在不同的内存区域,而不是分配到一块连续的内存中(出于一些我们现在不会讨论的理由)。vector 的这两部分(在堆上的和在栈上的)在任何时候都必须同步像大小,容量这样的信息。 104 | 105 | 当我们从`v`移动到`v2`,Rust 实际上按位拷贝了`v`在栈上分配的内容到`v2`。这个浅拷贝(shallow copy)并没有复制一份堆上分配的实际数据。这就是说将会有两个 vector 内容的指针都指向通用的堆上分配的空间。这样会违反 Rust 的安全保证,通过引入一个数据竞争,当你可以同时访问`v`和`v2`的时候。 106 | 107 | 例如如果我们通过`v2`缩短 vector 到只有两个元素: 108 | 109 | ```rust 110 | # let v = vec![1, 2, 3]; 111 | # let mut v2 = v; 112 | v2.truncate(2); 113 | ``` 114 | 115 | 而同时`v`仍是可以访问的话,这会产生一个无效的 vector,因为它并不知道堆上的数据已经被缩短了。现在`v`在栈上的部分与堆上的相应部分的信息并不一致。`v`仍然认为有 3 个元素并乐意我们访问那个并不存在的元素`v[2]`,不过你可能已经知道这是一个导致灾难的剧本。因为这可能会导致一个段错误,更糟的是会允许未经授权的用户读取他没有访问权限的数据。 116 | 117 | 这就是为何 Rust 在我们移动后禁止使用`v`的原因。 118 | 119 | 注意到优化可能会根据情况移除栈上字节(例如上面的向量)的实际拷贝也是很重要的。所以它也许并不像它开始看起来那样没有效率。 120 | 121 | ## `Copy`类型 122 | 123 | 我们已经知道了当所有权被转移给另一个绑定以后,你不能再使用原始绑定。然而,有一个[trait](Traits.md)会改变这个行为,它叫做`Copy`。我们还没有讨论到 trait,不过目前,你可以理解为一个为特定类型增加额外行为的标记。例如: 124 | 125 | ```rust 126 | let v = 1; 127 | 128 | let v2 = v; 129 | 130 | println!("v is: {}", v); 131 | ``` 132 | 133 | 在这个情况,`v`是一个`i32`,它实现了`Copy`。这意味着,就像一个移动,当我们把`v`赋值给`v2`,产生了一个数据的拷贝。不过,不像一个移动,我们仍可以在之后使用`v`。这是因为`i32`并没有指向其它数据的指针,对它的拷贝是一个完整的拷贝。 134 | 135 | 所有基本类型都实现了`Copy` trait,因此他们的所有权并不像你想象的那样遵循“所有权规则”被移动。作为一个例子,如下两段代码能够编译是因为`i32`和`bool`类型实现了`Copy` trait。 136 | 137 | ```rust 138 | fn main() { 139 | let a = 5; 140 | 141 | let _y = double(a); 142 | println!("{}", a); 143 | } 144 | 145 | fn double(x: i32) -> i32 { 146 | x * 2 147 | } 148 | ``` 149 | 150 | ```rust 151 | fn main() { 152 | let a = true; 153 | 154 | let _y = change_truth(a); 155 | println!("{}", a); 156 | } 157 | 158 | fn change_truth(x: bool) -> bool { 159 | !x 160 | } 161 | ``` 162 | 163 | 如果我们使用了没有实现`Copy`trait的类型,我们会得到一个编译错误,因为我们尝试使用一个移动了的值。 164 | 165 | ```text 166 | error: use of moved value: `a` 167 | println!("{}", a); 168 | ^ 169 | ``` 170 | 171 | 我们会在[trait](Traits.md)部分讨论如何编写你自己类型的`Copy`。 172 | 173 | ## 所有权之外(More than ownership) 174 | 175 | 当然,如果我们不得不在每个我们写的函数中交还所有权: 176 | 177 | ```rust 178 | fn foo(v: Vec) -> Vec { 179 | // Do stuff with `v`. 180 | 181 | // // Hand back ownership. 182 | v 183 | } 184 | ``` 185 | 186 | 这将会变得烦人。它在我们获取更多变量的所有权时变得更糟: 187 | 188 | ```rust 189 | fn foo(v1: Vec, v2: Vec) -> (Vec, Vec, i32) { 190 | // Do stuff with `v1` and `v2`. 191 | 192 | // Hand back ownership, and the result of our function. 193 | (v1, v2, 42) 194 | } 195 | 196 | let v1 = vec![1, 2, 3]; 197 | let v2 = vec![1, 2, 3]; 198 | 199 | let (v1, v2, answer) = foo(v1, v2); 200 | ``` 201 | 202 | 额!返回值,返回的代码行(上面的最后一行),和函数调用都变得更复杂了。 203 | 204 | 幸运的是,Rust 提供了一个 trait,借用,它帮助我们解决这个问题。这个主题将在下一个部分讨论! 205 | -------------------------------------------------------------------------------- /content/Variable Bindings 变量绑定.md: -------------------------------------------------------------------------------- 1 | # 变量绑定 2 | 3 | > [variable-bindings.md](https://github.com/rust-lang/book/blob/master/first-edition/src/variable-bindings.md) 4 | >
5 | > commit 23a7a7bdb6a6a43cd7efdd9176b1d3f75d9d0e70 6 | 7 | 事实上每一个非“Hello World” Rust 程序都用了**变量绑定**。他们将一些值绑定到一个名字上,这样可以在之后使用他们。`let`被用来声明一个绑定,像这样: 8 | 9 | ```rust 10 | fn main() { 11 | let x = 5; 12 | } 13 | ``` 14 | 15 | 在每个例子中都写上`fn main() {`有点冗长,所以之后我们将省略它。如果你是一路看过来的,确保你写了`main()`函数,而不是省略不写。否则,你将得到一个错误。 16 | 17 | ## 模式(Patterns) 18 | 19 | 在许多语言中,这叫做**变量**。不过 Rust 的变量绑定有一些不同的巧妙之处。例如`let`语句的左侧是一个“[模式](Patterns 模式.md)”,而不仅仅是一个变量。这意味着我们可以这样写: 20 | 21 | ```rust 22 | let (x, y) = (1, 2); 23 | ``` 24 | 25 | 在这个语句被计算后,`x`将会是1,而`y`将会是2。模式非常强大,并且本书中有[关于它的部分](Patterns 模式.md)。我们现在还不需要这些功能,所以接下来你只需记住有这个东西就行了。 26 | 27 | ## 类型注解(Type annotations) 28 | 29 | Rust 是一个静态类型语言,这意味着我们需要先确定我们需要的类型。那为什么我们第一个例子能编译过呢?好的,Rust有一个叫做**类型推断**的功能。如果它能确认这是什么类型,Rust 不需要你明确地指出来。 30 | 31 | 若你愿意,我们也可以加上类型。类型写在一个冒号(`:`)后面: 32 | 33 | ```rust 34 | let x: i32 = 5; 35 | ``` 36 | 37 | 如果我叫你对着全班同学大声读出这一行,你应该大喊“`x`被绑定为`i32`类型,它的值是`5`”。 38 | 39 | 在这个例子中我们选择`x`代表一个 32 位的有符号整数。Rust 有许多不同的原生整数类型。以`i`开头的代表有符号整数而`u`开头的代表无符号整数。可能的整数大小是 8、16、32 和 64 位。 40 | 41 | 在之后的例子中,我们可能会在注释中注明变量类型。例子看起来像这样: 42 | 43 | ```rust 44 | fn main() { 45 | let x = 5; // x: i32 46 | } 47 | ``` 48 | 49 | 注意注释和`let`表达式有类似的语法。理想的 Rust 代码中不应包含这类注释。不过我们偶尔会这么做来帮助你理解 Rust 推断的是什么类型。 50 | 51 | ## 可变性(Mutability) 52 | 53 | 绑定默认是**不可变的**(*immutable*)。下面的代码将不能编译: 54 | 55 | ```rust 56 | let x = 5; 57 | x = 10; 58 | ``` 59 | 60 | 它会给你如下错误: 61 | 62 | ```text 63 | error: re-assignment of immutable variable `x` 64 | x = 10; 65 | ^~~~~~~ 66 | ``` 67 | 68 | 如果你想一个绑定是可变的,使用`mut`: 69 | 70 | ```rust 71 | let mut x = 5; // mut x: i32 72 | x = 10; 73 | ``` 74 | 75 | 不止一个理由使得绑定默认不可变的,不过我们可以通过一个 Rust 的主要目标来理解它:安全。如果你没有使用`mut`,编译器会捕获它,让你知道你改变了一个你可能并不打算让它改变的值。如果绑定默认是可变的,编译器将不可能告诉你这些。如果你确实想变量可变,解决办法也非常简单:加个`mut`。 76 | 77 | 尽量避免可变状态有一些其它好处,不过这不在这个教程的讨论范围内。大体上,你总是可以避免显式可变量,并且这也是 Rust 希望你做的。即便如此,有时,可变量是你需要的,所以这并不是被禁止的。 78 | 79 | ## 初始化绑定(Initializing bindings) 80 | 81 | Rust 变量绑定有另一个不同于其它语言的方面:绑定要求在可以使用它之前必须初始化。 82 | 83 | 让我们尝试一下。将你的`src/main.rs`修改为为如下: 84 | 85 | ```rust 86 | fn main() { 87 | let x: i32; 88 | 89 | println!("Hello world!"); 90 | } 91 | ``` 92 | 93 | 你可以用`cargo build`命令去构建它。它依然会输出“Hello, world!”,不过你会得到一个警告: 94 | 95 | ```text 96 | Compiling hello_world v0.0.1 (file:///home/you/projects/hello_world) 97 | src/main.rs:2:9: 2:10 warning: unused variable: `x`, #[warn(unused_variables)] on by default 98 | src/main.rs:2 let x: i32; 99 | ^ 100 | ``` 101 | 102 | Rust 警告我们从未使用过这个变量绑定,但是因为我们从未用过它,无害不罚。然而,如果你确实想使用`x`,事情就不一样了。让我们试一下。修改代码如下: 103 | 104 | ```rust 105 | fn main() { 106 | let x: i32; 107 | 108 | println!("The value of x is: {}", x); 109 | } 110 | ``` 111 | 112 | 然后尝试构建它。你会得到一个错误: 113 | 114 | ```bash 115 | $ cargo build 116 | Compiling hello_world v0.0.1 (file:///home/you/projects/hello_world) 117 | src/main.rs:4:39: 4:40 error: use of possibly uninitialized variable: `x` 118 | src/main.rs:4 println!("The value of x is: {}", x); 119 | ^ 120 | note: in expansion of format_args! 121 | :2:23: 2:77 note: expansion site 122 | :1:1: 3:2 note: in expansion of println! 123 | src/main.rs:4:5: 4:42 note: expansion site 124 | error: aborting due to previous error 125 | Could not compile `hello_world`. 126 | ``` 127 | 128 | Rust 是不会让我们使用一个没有经过初始化的值的。 129 | 130 | 让我们讨论一下我们添加到`println!`中的内容。 131 | 132 | 如果你输出的字符串中包含一对大括号(`{}`,一些人称之为胡须。。(译注:moustaches,八字胡)),Rust将把它解释为插入值的请求。**字符串插值**(*String interpolation*)是一个计算机科学术语,代表“在字符串中插入值”。我们加上一个逗号,然后是一个`x`,来表示我们想插入`x`的值。逗号用来分隔我们传递给函数和宏的参数,如果你想传递多个参数的话。 133 | 134 | 当你只写了大括号的时候,Rust 会尝试检查值的类型来显示一个有意义的值。如果你想指定详细的语法,有[很多选项可供选择](http://doc.rust-lang.org/std/fmt/)。现在,让我们保持默认格式,整数并不难打印。 135 | 136 | ## 作用域和隐藏(Scope and shadowing) 137 | 138 | 让我们回到绑定的话题上。变量绑定有一个作用域 - 他们被限制只能在他们被定义的块中存在。一个块是一个被`{`和`}`包围的语句集合。函数定义也是块!在下面的例子中我们定义了两个变量绑定,`x`和`y`,他们位于不同的作用域中。`x`可以在`fn main() {}`块中被访问,而`y`只能在内部块内访问: 139 | 140 | ```rust 141 | fn main() { 142 | let x: i32 = 17; 143 | { 144 | let y: i32 = 3; 145 | println!("The value of x is {} and value of y is {}", x, y); 146 | } 147 | println!("The value of x is {} and value of y is {}", x, y); // This won't work. 148 | } 149 | ``` 150 | 151 | 第一个`println!`将会打印“The value of x is 17 and the value of y is 3”,不过这个并不能编译成功,因为第二个`println!`并不能访问`y`的值,因为它已不在作用域中。相反我们得到如下错误: 152 | 153 | ```bash 154 | $ cargo build 155 | Compiling hello v0.1.0 (file:///home/you/projects/hello_world) 156 | main.rs:7:62: 7:63 error: unresolved name `y`. Did you mean `x`? [E0425] 157 | main.rs:7 println!("The value of x is {} and value of y is {}", x, y); // This won't work. 158 | ^ 159 | note: in expansion of format_args! 160 | :2:25: 2:56 note: expansion site 161 | :1:1: 2:62 note: in expansion of print! 162 | :3:1: 3:54 note: expansion site 163 | :1:1: 3:58 note: in expansion of println! 164 | main.rs:7:5: 7:65 note: expansion site 165 | main.rs:7:62: 7:63 help: run `rustc --explain E0425` to see a detailed explanation 166 | error: aborting due to previous error 167 | Could not compile `hello`. 168 | 169 | To learn more, run the command again with --verbose. 170 | ``` 171 | 172 | 另外,变量可以被隐藏。这意味着一个后声明的并位于同一作用域的相同名字的变量绑定将会覆盖前一个变量绑定: 173 | 174 | ```rust 175 | let x: i32 = 8; 176 | { 177 | println!("{}", x); // Prints "8". 178 | let x = 12; 179 | println!("{}", x); // Prints "12". 180 | } 181 | println!("{}", x); // Prints "8". 182 | let x = 42; 183 | println!("{}", x); // Prints "42". 184 | ``` 185 | 186 | 隐藏和可变绑定可能表现为同一枚硬币的两面,他们是两个不同的概念,不能互换使用。举个例子,隐藏允许我们将一个名字重绑定为不同的类型。它也可以改变一个绑定的可变性。注意隐藏并不改变和销毁被绑定的值,这个值会在离开作用域之前继续存在,即便无法通过任何手段访问到它。 187 | 188 | ```rust 189 | let mut x: i32 = 1; 190 | x = 7; 191 | let x = x; // `x` is now immutable and is bound to `7` 192 | 193 | let y = 4; 194 | let y = "I can also be bound to text!"; // `y` is now of a different type 195 | ``` 196 | -------------------------------------------------------------------------------- /content/Bibliography 参考文献.md: -------------------------------------------------------------------------------- 1 | # 参考文献 2 | 3 | > [bibliography.md](https://github.com/rust-lang/book/blob/master/first-edition/src/bibliography.md) 4 | >
5 | > commit 090fcb88b1c7353e480ce3aff0028832e9bb696b 6 | 7 | 这是一个与 Rust 相关的材料的阅读列表。这包含了曾经影响过 Rust 先验研究,以及关于 Rust 的出版物。 8 | 9 | > **(注:以下翻译属个人理解,勿作为参考!)** 10 | 11 | ## 类型系统 12 | 13 | * [Cyclone语言中基于区域的内存管理(Region based memory management in Cyclone)](http://209.68.42.137/ucsd-pages/Courses/cse227.w03/handouts/cyclone-regions.pdf) 14 | * [Cyclone语言中的手动安全内存管理(Safe manual memory management in Cyclone)](http://www.cs.umd.edu/projects/PL/cyclone/scp.pdf) 15 | * [类型类:使临时多态不再临时(Typeclasses: making ad-hoc polymorphism less ad hoc)](http://www.ps.uni-sb.de/courses/typen-ws99/class.ps.gz) 16 | * [宏综述(Macros that work together)](https://www.cs.utah.edu/plt/publications/jfp12-draft-fcdf.pdf) 17 | * [特性:组合类型的行为(Traits: composable units of behavior)](http://scg.unibe.ch/archive/papers/Scha03aTraits.pdf) 18 | * [消除别名(Alias burying)](http://www.cs.uwm.edu/faculty/boyland/papers/unique-preprint.ps) - 我们尝试了一些相似的内容并放弃了它 19 | * [外部唯一性是足够的(External uniqueness is unique enough)](http://www.computingscience.nl/research/techreps/repo/CS-2002/2002-048.pdf) 20 | * [用于安全并行的唯一性和引用不可变性(Uniqueness and Reference Immutability for Safe Parallelism)](https://research.microsoft.com/pubs/170528/msr-tr-2012-79.pdf) 21 | * [基于区域的内存管理(Region Based Memory Management)](http://www.cs.ucla.edu/~palsberg/tba/papers/tofte-talpin-iandc97.pdf) 22 | 23 | ## 并发 24 | 25 | * [Singularity:软件栈的重新思考(Singularity: rethinking the software stack)](https://research.microsoft.com/pubs/69431/osr2007_rethinkingsoftwarestack.pdf) 26 | * [Singularity操作系统中支持快速和可靠的消息传递的语言(Language support for fast and reliable message passing in singularity OS)](https://research.microsoft.com/pubs/67482/singsharp.pdf) 27 | * [通过work stealing来安排多线程计算(Scheduling multithreaded computations by work stealing)](http://supertech.csail.mit.edu/papers/steal.pdf) 28 | * [多道程序多处理器的线程调度(Thread scheduling for multiprogramming multiprocessors)](http://www.eecis.udel.edu/~cavazos/cisc879-spring2008/papers/arora98thread.pdf) 29 | * [work stealing中的数据局部性(The data locality of work stealing)](http://www.aladdin.cs.cmu.edu/papers/pdfs/y2000/locality_spaa00.pdf) 30 | * [动态环形work stealing双端队列(Dynamic circular work stealing deque)](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.170.1097&rep=rep1&type=pdf) - Chase/Lev双端队列 31 | * [异步-完成并行的work优先和help优先的调度策略(Work-first and help-first scheduling policies for async-finish task parallelism)](http://www.cs.rice.edu/~yguo/pubs/PID824943.pdf) - 比严格的work stealing更宽泛 32 | * [一个Java的fork/join灾难(A Java fork/join calamity)](http://www.coopsoft.com/ar/CalamityArticle.html) - 对Java fork/join库的批判,特别是其在非严格计算时的work stealing实现 33 | * [并发系统的调度技巧(Scheduling techniques for concurrent systems)](http://www.ece.rutgers.edu/~parashar/Classes/ece572-papers/05/ps-ousterhout.pdf) 34 | * [竞争启发调度(Contention aware scheduling)](http://www.blagodurov.net/files/a8-blagodurov.pdf) 35 | * [时间共享多核系统的平衡work stealing(Balanced work stealing for time-sharing multicores)](http://www.cse.ohio-state.edu/hpcs/WWW/HTML/publications/papers/TR-12-1.pdf) 36 | * [三层蛋糕?(Three layer cake)](http://www.upcrc.illinois.edu/workshops/paraplop10/papers/paraplop10_submission_8.pdf) 37 | * [非阻塞半work stealing队列(Non-blocking steal-half work queues)](http://www.cs.bgu.ac.il/~hendlerd/papers/p280-hendler.pdf) 38 | * [Reagents:表现和编写细粒度的并发(Reagents: expressing and composing fine-grained concurrency)](http://www.mpi-sws.org/~turon/reagents.pdf) 39 | * [用于共享内存多处理器的可扩展同步性的算法(Algorithms for scalable synchronization of shared-memory multiprocessors)](https://www.cs.rochester.edu/u/scott/papers/1991_TOCS_synch.pdf) 40 | * [Epoch-based reclamation](https://www.cl.cam.ac.uk/techreports/UCAM-CL-TR-579.pdf). 41 | 42 | ## 其它 43 | 44 | * [只能崩溃的软件(Crash-only software)](https://www.usenix.org/legacy/events/hotos03/tech/full_papers/candea/candea.pdf) 45 | * [编写高性能内存分配器(Composing High-Performance Memory Allocators)](http://people.cs.umass.edu/~emery/pubs/berger-pldi2001.pdf) 46 | * [对手动内存分配的思考(Reconsidering Custom Memory Allocation)](http://people.cs.umass.edu/~emery/pubs/berger-oopsla2002.pdf) 47 | 48 | ## **关于** Rust 的论文 49 | 50 | * [Rust中的GPU编程(GPU programming in Rust)](http://www.cs.indiana.edu/~eholk/papers/hips2013.pdf) 51 | * [并行闭包:一个基于老观点的新做法(Parallel closures: a new twist on an old idea)](https://www.usenix.org/conference/hotpar12/parallel-closures-new-twist-old-idea) - 并不完全关于Rust,不过是Nicholas D. Matsakis写的 52 | * [Patina: A Formalization of the Rust Programming Language](ftp://ftp.cs.washington.edu/tr/2015/03/UW-CSE-15-03-02.pdf)。一类型系统子集的早期形式,Eric Reed著。 53 | * [Experience Report: Developing the Servo Web Browser Engine using Rust](http://arxiv.org/abs/1505.07383)。Lars Bergstrom著。 54 | * [Implementing a Generic Radix Trie in Rust](https://michaelsproul.github.io/rust_radix_paper/rust-radix-sproul.pdf)。Michael Sproul的毕业论文。 55 | * [Reenix: Implementing a Unix-Like Operating System in Rust](http://scialex.github.io/reenix.pdf)。Alex Light的毕业论文。 56 | * [Evaluation of performance and productivity metrics of potential programming languages in the HPC environment](http://doc.rust-lang.org/stable/book/academic-research.html)。Florian Wilkens的学士学位论文。比较C,Go和Rust。 57 | * [Nom, a byte oriented, streaming, zero copy, parser combinators library in Rust](http://spw15.langsec.org/papers/couprie-nom.pdf)。Geoffroy Couprie著,关于VLC的研究。 58 | * [Graph-Based Higher-Order Intermediate Representation](http://compilers.cs.uni-saarland.de/papers/lkh15_cgo.pdf)。一个用Impala(一个类似Rust的语言)实现的实验性的IR。 59 | * [Code Refinement of Stencil Codes](http://compilers.cs.uni-saarland.de/papers/ppl14_web.pdf)。另一个使用Impala的论文。 60 | * [Parallelization in Rust with fork-join and 61 | friends](http://publications.lib.chalmers.se/records/fulltext/219016/219016.pdf). Linus 62 | Farnstrand's master's thesis. 63 | * [Session Types for 64 | Rust](http://munksgaard.me/papers/laumann-munksgaard-larsen.pdf). Philip 65 | Munksgaard's master's thesis. Research for Servo. 66 | * [Ownership is Theft: Experiences Building an Embedded OS in Rust - Amit Levy, et. al.](http://amitlevy.com/papers/tock-plos2015.pdf) 67 | * [You can't spell trust without Rust](https://raw.githubusercontent.com/Gankro/thesis/master/thesis.pdf). Alexis Beingessner's master's thesis. 68 | -------------------------------------------------------------------------------- /content/Primitive Types 原生类型.md: -------------------------------------------------------------------------------- 1 | # 原生类型 2 | 3 | > [primitive-types.md](https://github.com/rust-lang/book/blob/master/first-edition/src/primitive-types.md) 4 | >
5 | > commit 23a7a7bdb6a6a43cd7efdd9176b1d3f75d9d0e70 6 | 7 | Rust 有一系列被认为是“原生”的类型。这意味着它们是内建在语言中的。Rust被构建为在标准库中也提供了一些建立在这些类型之上的有用的类型,不过它们也大部分是原生的。 8 | 9 | ## 布尔型 10 | 11 | Rust 有一个内建的布尔类型,叫做`bool`。它有两个值,`true`和`false`: 12 | 13 | ```rust 14 | let x = true; 15 | 16 | let y: bool = false; 17 | ``` 18 | 19 | 布尔型通常用在[if语句](If If语句.md)中。 20 | 21 | 你可以在[标准库文档](http://doc.rust-lang.org/nightly/std/primitive.bool.html)中找到更多关于`bool`的文档。 22 | 23 | ## `char` 24 | `char`类型代表一个单独的 Unicode 字符的值。你可以用单引号(`'`)创建`char`: 25 | 26 | ```rust 27 | let x = 'x'; 28 | let two_hearts = '💕'; 29 | ``` 30 | 31 | 不像其它语言,这意味着Rust的`char`并不是 1 个字节,而是 4 个。 32 | 33 | 你可以在[标准库文档](http://doc.rust-lang.org/nightly/std/primitive.char.html)中找到更多关于`char`的文档。 34 | 35 | ## 数字类型 36 | 37 | Rust有一些分类的大量数字类型:有符号和无符号,定长和变长,浮点和整型。 38 | 39 | 这些类型包含两部分:分类,和大小。例如,`u16`是一个拥有 16 位大小的无符号类型。更多字节让你拥有更大的数字。 40 | 41 | 如果一个数字常量没有推断它类型的条件,它采用默认类型: 42 | 43 | ```rust 44 | let x = 42; // `x` has type `i32`. 45 | 46 | let y = 1.0; // `y` has type `f64`. 47 | ``` 48 | 49 | 这里有一个不同数字类型的列表,以及它们在标准库中的文档: 50 | 51 | * [i8](http://doc.rust-lang.org/nightly/std/primitive.i8.html) 52 | * [i16](http://doc.rust-lang.org/nightly/std/primitive.i16.html) 53 | * [i32](http://doc.rust-lang.org/nightly/std/primitive.i32.html) 54 | * [i64](http://doc.rust-lang.org/nightly/std/primitive.i64.html) 55 | * [u8](http://doc.rust-lang.org/nightly/std/primitive.u8.html) 56 | * [u16](http://doc.rust-lang.org/nightly/std/primitive.u16.html) 57 | * [u32](http://doc.rust-lang.org/nightly/std/primitive.u32.html) 58 | * [u64](http://doc.rust-lang.org/nightly/std/primitive.u64.html) 59 | * [isize](http://doc.rust-lang.org/nightly/std/primitive.isize.html) 60 | * [usize](http://doc.rust-lang.org/nightly/std/primitive.usize.html) 61 | * [f32](http://doc.rust-lang.org/nightly/std/primitive.f32.html) 62 | * [f64](http://doc.rust-lang.org/nightly/std/primitive.f64.html) 63 | 64 | 让我们按分类重温一遍: 65 | 66 | ### 有符号和无符号 67 | 68 | 整型有两种变体:有符号和无符号。为了理解它们的区别,让我们考虑一个 4 比特大小的数字。一个有符号,4 比特数字你可以储存`-8`到`+7`的数字。有符号数采用“二进制补码”表示。一个无符号 4 比特的数字,因为它不需要储存负数,可以出储存`0`到`+15`的数字。 69 | 70 | 无符号类型使用`u`作为他们的分类,而有符号类型使用`i`。`i`代表“integer”。所以`u8`是一个 8 位无符号数字,而`i8`是一个 8 位有符号数字。 71 | 72 | ### 固定大小类型 73 | 74 | 固定大小类型在其表现中有特定数量的位。有效的位大小是`8`,`16`,`32`和`64`。那么,`u32`是无符号的,32 位整型,而`i64`是有符号,64 位整型。 75 | 76 | ### 可变大小类型 77 | 78 | Rust 也提供了依赖底层机器指针大小的类型。这些类型拥有“size”分类,并有有符号和无符号变体。它有两个类型:`isize`和`usize`。 79 | 80 | ### 浮点类型 81 | 82 | Rust 也有两个浮点类型:`f32`和`f64`。它们对应 IEEE-754 单精度和双精度浮点数。 83 | 84 | ## 数组 85 | 86 | 像很多编程语言一样,Rust有用来表示数据序列的列表类型。最基本的是**数组**,一个定长相同类型的元素列表。数组默认是不可变的。 87 | 88 | ```rust 89 | let a = [1, 2, 3]; // a: [i32; 3] 90 | let mut m = [1, 2, 3]; // m: [i32; 3] 91 | ``` 92 | 93 | 数组的类型是`[T; N]`。我们会在[泛型部分](Generics 泛型.md)的时候讨论这个`T`标记。`N`是一个编译时常量,代表数组的长度。 94 | 95 | 有一个可以将数组中每一个元素初始化为相同值的简写。在这个例子中,`a`的每个元素都被初始化为`0`: 96 | 97 | ```rust 98 | let a = [0; 20]; // a: [i32; 20] 99 | ``` 100 | 101 | 你可以用`a.len()`来获取数组`a`的元素数量: 102 | 103 | ```rust 104 | let a = [1, 2, 3]; 105 | 106 | println!("a has {} elements", a.len()); 107 | ``` 108 | 109 | 你可以用**下标**(*subscript notation*)来访问特定的元素: 110 | 111 | ```rust 112 | let names = ["Graydon", "Brian", "Niko"]; // names: [&str; 3] 113 | 114 | println!("The second name is: {}", names[1]); 115 | ``` 116 | 117 | 就跟大部分编程语言一个样,下标从0开始,所以第一个元素是`names[0]`,第二个是`names[1]`。上面的例子会打印出`The second name is: Brian`。如果你尝试使用一个不在数组中的下标,你会得到一个错误:数组访问会在运行时进行边界检查。这种不适当的访问是其它系统编程语言中很多bug的根源。 118 | 119 | 你可以在[标准库文档](http://doc.rust-lang.org/stable/std/primitive.array.html)中找到更多关于`array`的文档。 120 | 121 | ## 切片(Slices) 122 | 123 | 一个**切片**(*slice*)是一个数组的引用(或者“视图”)。它有利于安全,有效的访问数组的一部分而不用进行拷贝。比如,你可能只想要引用读入到内存的文件中的一行。原理上,片段并不是直接创建的,而是引用一个已经存在的变量。片段有预定义的长度,可以是可变也可以是不可变的。 124 | 125 | 在底层,slice 代表一个指向数据开始的指针和一个长度。 126 | 127 | ### 切片语法(Slicing syntax) 128 | 129 | 你可以用一个`&`和`[]`的组合从多种数据类型创建一个切片。`&`表明切片类似于[引用](References and Borrowing 引用和借用.md),这个我们会在本部分的后面详细介绍。带有一个范围的`[]`,允许你定义切片的长度: 130 | 131 | ```rust 132 | let a = [0, 1, 2, 3, 4]; 133 | let complete = &a[..]; // A slice containing all of the elements in `a`. 134 | let middle = &a[1..4]; // A slice of `a`: only the elements `1`, `2`, and `3`. 135 | ``` 136 | 137 | 片段拥有`&[T]`类型。当我们涉及到[泛型](Generics 泛型.md)时会讨论这个`T`。 138 | 139 | 你可以在[标准库文档](http://doc.rust-lang.org/stable/std/primitive.slice.html)中找到更多关于`slices`的文档。 140 | 141 | 142 | ## `str` 143 | 144 | Rust的`str`类型是最原始的字符串类型。作为一个[不定长类型](Unsized Types 不定长类型.md),它本身并不是非常有用,不过当它用在引用后是就有用了,例如[&str](Strings 字符串.md)。如你所见,我们到时候再讲。 145 | 146 | 你可以在[标准库文档](http://doc.rust-lang.org/stable/std/primitive.str.html)中找到更多关于`str`的文档。 147 | 148 | ## 元组(Tuples) 149 | 150 | 元组(tuples)是固定大小的有序列表。如下: 151 | 152 | ```rust 153 | let x = (1, "hello"); 154 | ``` 155 | 156 | 这是一个长度为 2 的元组,由括号和逗号组成。下面也是同样的元组,不过注明了数据类型: 157 | 158 | ```rust 159 | let x: (i32, &str) = (1, "hello"); 160 | ``` 161 | 162 | 如你所见,元组的类型跟元组看起来很像,只不过类型取代的值的位置。细心的读者可能会注意到元组是异质的:这个元组中有一个`i32`和一个`&str`。在系统编程语言中,字符串要比其它语言中来的复杂。现在,可以认为`&str`是一个**字符串片段**(*string slice*),我们马上会讲到它。 163 | 164 | 你可以把一个元组赋值给另一个,如果它们包含相同的类型和[数量](Glossary 词汇表.md#参数数量(arity))。当元组有相同的长度时它们有相同的数量。 165 | 166 | ```rust 167 | let mut x = (1, 2); // x: (i32, i32) 168 | let y = (2, 3); // y: (i32, i32) 169 | 170 | x = y; 171 | ``` 172 | 173 | 你可以通过一个**解构let**(*destructuring let*)访问元组中的字段。下面是一个例子: 174 | 175 | ```rust 176 | let (x, y, z) = (1, 2, 3); 177 | 178 | println!("x is {}", x); 179 | ``` 180 | 181 | 还记得[之前](Variable Bindings 变量绑定.md)我曾经说过`let`语句的左侧远比一个赋值绑定强大吗?这就是证据。我们可以在`let`左侧写一个模式,如果它能匹配右侧的话,我们可以一次写多个绑定。这种情况下,`let`“解构”或“拆开”了元组,并分成了三个绑定。 182 | 183 | 这个模式是很强大的,我们后面会经常看到它。 184 | 185 | 你可以一个逗号来消除一个单元素元组和一个括号中的值的歧义: 186 | 187 | ```rust 188 | (0,); // single-element tuple 189 | (0); // zero in parentheses 190 | ``` 191 | 192 | ### 元组索引(Tuple Indexing) 193 | 194 | 你也可以用索引语法访问一个元组的字段: 195 | 196 | ```rust 197 | let tuple = (1, 2, 3); 198 | 199 | let x = tuple.0; 200 | let y = tuple.1; 201 | let z = tuple.2; 202 | 203 | println!("x is {}", x); 204 | ``` 205 | 206 | 就像数组索引,它从`0`开始,不过也不像数组索引,它使用`.`,而不是`[]`。 207 | 208 | 你可以在[标准库文档](http://doc.rust-lang.org/stable/std/primitive.tuple.html)中找到更多关于`tuple`的文档。 209 | 210 | ## 函数 211 | 212 | 函数也有一个类型!它们看起来像这样: 213 | 214 | ```rust 215 | fn foo(x: i32) -> i32 { x } 216 | 217 | let x: fn(i32) -> i32 = foo; 218 | ``` 219 | 220 | 在这个例子中,`x`是一个“函数指针”,指向一个获取一个`i32`参数并返回一个`i32`值的函数。 221 | -------------------------------------------------------------------------------- /content/Procedural Macros 过程宏.md: -------------------------------------------------------------------------------- 1 | # 过程宏(和自定义导出) 2 | 3 | > [procedural-macros.md](https://github.com/rust-lang/book/blob/master/first-edition/src/procedural-macros.md) 4 | >
5 | > commit 399708cf7bb12f593ad4bb403e94466d146e743e 6 | 7 | 在本书接下来的部分,你将看到 Rust 提供了一个叫做“导出(derive)”的机制来轻松的实现 trait。例如, 8 | 9 | ```rust 10 | #[derive(Debug)] 11 | struct Point { 12 | x: i32, 13 | y: i32, 14 | } 15 | ``` 16 | 17 | is a lot simpler than 18 | 19 | ```rust 20 | struct Point { 21 | x: i32, 22 | y: i32, 23 | } 24 | 25 | use std::fmt; 26 | 27 | impl fmt::Debug for Point { 28 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 29 | write!(f, "Point {{ x: {}, y: {} }}", self.x, self.y) 30 | } 31 | } 32 | ``` 33 | 34 | Rust 包含很多可以导出的 trait,不过也允许定义你自己的 trait。我们可以通过一个叫做“过程宏”的 Rust 功能来实现这个效果。最终,过程宏将会允许 Rust 所有类型的高级元编程,不过现在只能自定义导出。 35 | 36 | ## Hello World 37 | 38 | 首先需要做的就是为我们的项目新建一个 crate。 39 | 40 | ```bash 41 | $ cargo new --bin hello-world 42 | ``` 43 | 44 | 我们想要实现的就是能够在导出的类型上调用`hello_world()`。就想这样: 45 | 46 | ```rust,ignore 47 | #[derive(HelloWorld)] 48 | struct Pancakes; 49 | 50 | fn main() { 51 | Pancakes::hello_world(); 52 | } 53 | ``` 54 | 55 | 再来一些给力的输出,比如“Hello, World! 我叫煎饼(←_←)。” 56 | 57 | 继续并从用户的角度编写我们的宏。在`src/main.rs`中: 58 | 59 | ```rust,ignore 60 | #[macro_use] 61 | extern crate hello_world_derive; 62 | 63 | trait HelloWorld { 64 | fn hello_world(); 65 | } 66 | 67 | #[derive(HelloWorld)] 68 | struct FrenchToast; 69 | 70 | #[derive(HelloWorld)] 71 | struct Waffles; 72 | 73 | fn main() { 74 | FrenchToast::hello_world(); 75 | Waffles::hello_world(); 76 | } 77 | ``` 78 | 79 | 好的。现在我们只需实际编写我们的过程宏。目前,过程宏需要位于它自己的 crate 中。最终这个限制会解除,不过现在是必须的。为此,有一个惯例是,对于一个叫`foo`的 crate,一个自定义的过程宏叫做`foo-derive`。让我们在`hello-world`项目中新建一个叫做`hello-world-derive`的 crate。 80 | 81 | ```bash 82 | $ cargo new hello-world-derive 83 | ``` 84 | 85 | 为了确保`hello-world` crate 能够找到这个新创建的 crate 我们把它加入到项目 toml 文件中: 86 | 87 | ```toml 88 | [dependencies] 89 | hello-world-derive = { path = "hello-world-derive" } 90 | ``` 91 | 92 | 这里是一个`hello-world-derive` crate 源码的例子: 93 | 94 | ```rust,ignore 95 | extern crate proc_macro; 96 | extern crate syn; 97 | #[macro_use] 98 | extern crate quote; 99 | 100 | use proc_macro::TokenStream; 101 | 102 | #[proc_macro_derive(HelloWorld)] 103 | pub fn hello_world(input: TokenStream) -> TokenStream { 104 | // Construct a string representation of the type definition 105 | let s = input.to_string(); 106 | 107 | // Parse the string representation 108 | let ast = syn::parse_derive_input(&s).unwrap(); 109 | 110 | // Build the impl 111 | let gen = impl_hello_world(&ast); 112 | 113 | // Return the generated impl 114 | gen.parse().unwrap() 115 | } 116 | ``` 117 | 118 | 这里有很多内容。我们引入了两个新的 crate:[`syn`]和[`quote`]。你可能注意到了,`input: TokenSteam`直接就被转换成了一个`String`。这个字符串是我们要导出的`HelloWorld`Rust 代码的字符串形式。现在,能对`TokenStream`做的唯一的事情就是把它转换为一个字符串。将来会有更丰富的 API。 119 | 120 | 所以我们真正需要做的是能够把 Rust 代码**解析**成有用的东西。这正是`syn`出场机会。`syn`是一个解析 Rust 代码的 crate。我们引入的另外一个 crate 是`quote`。它本质上与`syn`是成双成对的,因为它可以轻松的生成 Rust 代码。也可以自己编写这些功能,不过使用这些库会更加轻松。编写一个完整 Rust 代码解析器可不是一个简单的工作。 121 | 122 | [`syn`]: https://crates.io/crates/syn 123 | [`quote`]: https://crates.io/crates/quote 124 | 125 | 这些代码注释提供了我们总体策略的很好的解释。我们将为导出的类型提供一个`String`类型的 Rust 代码,用`syn`解析它,(使用`quote`)构建`hello_world`的实现,接着把它传递回给 Rust 编译器。 126 | 127 | 最后一个要点:这里有一些`unwrap()`,如果你要为过程宏提供一个错误,那么你需要`panic!`并提供错误信息。这里,我们从简实现。 128 | 129 | 好的,让我们编写`impl_hello_world(&ast)`。 130 | 131 | ```rust,ignore 132 | fn impl_hello_world(ast: &syn::MacroInput) -> quote::Tokens { 133 | let name = &ast.ident; 134 | quote! { 135 | impl HelloWorld for #name { 136 | fn hello_world() { 137 | println!("Hello, World! My name is {}", stringify!(#name)); 138 | } 139 | } 140 | } 141 | } 142 | ``` 143 | 144 | 这里就是`quote`出场的地方。`ast`参数是一个代表我们类型(可以是一个`struct`或`enum`)的结构体。查看[文档](https://docs.rs/syn/0.10.5/syn/struct.MacroInput.html)。这里有一些有用的信息。我们可以通过`ast.ident`获取类型的信息。`quote!`宏允许我们编写想要返回的 Rust 代码并把它转换为`Tokens`。`quote!`让我们可以使用一些炫酷的模板机制;简单的使用`#name`,`quote!`就会把它替换为叫做`name`的变量。你甚至可以类似常规宏那样进行一些重复。请查看这些[文档](https://docs.rs/quote),这里有一些好的介绍。 145 | 146 | 应该就这些了。噢,对了,我们需要在`hello-world-derive` crate 的`cargo.toml`中添加`syn`和`quote`的依赖。 147 | 148 | ```toml 149 | [dependencies] 150 | syn = "0.10.5" 151 | quote = "0.3.10" 152 | ``` 153 | 154 | 这样就 OK 了。尝试编译`hello-world`。 155 | 156 | ```bash 157 | error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type 158 | --> hello-world-derive/src/lib.rs:8:3 159 | | 160 | 8 | #[proc_macro_derive(HelloWorld)] 161 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 162 | ``` 163 | 164 | 好吧,看来我们需要把`hello-world-derive` crate 声明为`proc-macro`类型。怎么做呢?像这样: 165 | 166 | 167 | ```toml 168 | [lib] 169 | proc-macro = true 170 | ``` 171 | 172 | 现在好了,编译`hello-world`。现在执行`cargo run`将会输出: 173 | 174 | ```bash 175 | Hello, World! My name is FrenchToast 176 | Hello, World! My name is Waffles 177 | ``` 178 | 179 | 我们成功了! 180 | 181 | ## 自定义 attribute(Custom Attributes) 182 | 183 | 在一些情况下允许用户进行一些配置是合理的。例如,用户可能想要重载`hello_world()`方法打印出的名字的值。 184 | 185 | 这可以通过自定义 attribute 来实现: 186 | 187 | ```rust 188 | #[derive(HelloWorld)] 189 | #[HelloWorldName = "the best Pancakes"] 190 | struct Pancakes; 191 | 192 | fn main() { 193 | Pancakes::hello_world(); 194 | } 195 | ``` 196 | 197 | 但是如果我们尝试编译它,编译器会返回一个错误: 198 | 199 | ```bash 200 | error: The attribute `HelloWorldName` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) 201 | ``` 202 | 203 | 编译器需要知道我们处理了这个 attribute 才能不返回错误。这可以通过在`hello-world-derive` crate 中对`proc_macro_derive` attribute 增加`attributes`来实现: 204 | 205 | ```rust,ignore 206 | #[proc_macro_derive(HelloWorld, attributes(HelloWorldName))] 207 | pub fn hello_world(input: TokenStream) -> TokenStream 208 | ``` 209 | 210 | 可以一同样的方式指定多个 attribute。 211 | 212 | ## 引发错误 213 | 214 | 让我们假设我们并不希望在我们的自定义导出方法中接受枚举作为输入。 215 | 216 | 这个条件可以通过`syn`轻松的进行检查。不过我们如何告诉用户,我们并不接受枚举呢?在过程宏中报告错误的传统做法是 panic: 217 | 218 | ```rust 219 | fn impl_hello_world(ast: &syn::MacroInput) -> quote::Tokens { 220 | let name = &ast.ident; 221 | // Check if derive(HelloWorld) was specified for a struct 222 | if let syn::Body::Struct(_) = ast.body { 223 | // Yes, this is a struct 224 | quote! { 225 | impl HelloWorld for #name { 226 | fn hello_world() { 227 | println!("Hello, World! My name is {}", stringify!(#name)); 228 | } 229 | } 230 | } 231 | } else { 232 | //Nope. This is an Enum. We cannot handle these! 233 | panic!("#[derive(HelloWorld)] is only defined for structs, not for enums!"); 234 | } 235 | } 236 | ``` 237 | 238 | 如果用户尝试从一个枚举导出`HelloWorld`,他们会收到如下希望有帮助的错误信息: 239 | 240 | 241 | ```bash 242 | error: custom derive attribute panicked 243 | --> src/main.rs 244 | | 245 | | #[derive(HelloWorld)] 246 | | ^^^^^^^^^^ 247 | | 248 | = help: message: #[derive(HelloWorld)] is only defined for structs, not for enums! 249 | ``` -------------------------------------------------------------------------------- /content/Rust Inside Other Languages 其它语言中的 Rust.md: -------------------------------------------------------------------------------- 1 | # 其他语言中的 Rust 2 | 3 | > [rust-inside-other-languages.md](https://github.com/rust-lang/rust/blob/stable/src/doc/book/rust-inside-other-languages.md) 4 | >
5 | > commit 024aa9a345e92aa1926517c4d9b16bd83e74c10d 6 | 7 | > **注:** 1.7.0-stable 将此章节去掉了,因此内容可能不具有时效性,这里我们暂时保留。 8 | 9 | 作为我们的第三个项目,我们决定选择一个可以展示Rust最强实力的功能:缺少实质的运行时。 10 | 11 | 当组织增长,他们越来越依赖大量的编程语言。不同的编程语言有不同的能力和弱点,而一个多语言栈让你在某个特定的编程语言的优点起作用的时候能使用它,当它有缺陷时使用其他编程语言。 12 | 13 | 一个非常常见的问题是很多编程语言在程序的运行时性能是很差的。通常来讲,使用一个更慢的,不过提供了更强大的程序员生产力的语言是一个值得的权衡的问题。为了帮助缓和这个问题,它们提供了一个用C编写部分你的系统,然后接着用高级语言编写的代码调用C代码。这叫做一个“外部函数接口”,通常简写为“FFI”。 14 | 15 | Rust 在所有两个方向支持 FFI:它可以简单的调用C代码,而且至关重要的,它也可以简单的**在**C中被调用。与 Rust 缺乏垃圾回收和底层运行时要求相结合,这让 Rust 成为一个当你需要嵌入其他语言中以提供一些额外的活力时的强大的候选。 16 | 17 | 这有一个全面[专注于FFI的章节](4.9.Foreign Function Interface 外部函数接口.md)和详情位于本书的其他位置。不过在这一章,我们会检查这个特定的FFI用例,通过 3 个例子,分别在 Ruby,Python 和 JavaScript 中。 18 | 19 | ## 问题 20 | 有很多不同的项目我们可以选择,不过我们将选择一个 Rust 相比其他语言有明确优势的例子:数值计算和线程。 21 | 22 | 很多语言,为了一致性,将数字放在堆上,而不是放在栈上。特别是在专注面向对象编程和使用垃圾回收的语言中,堆分配是默认行为。有时优化会栈分配特定的数字,不过与其依赖优化器做这个工作,我们可能想要确保我们总是使用原始类型而不是使用各种对象类型。 23 | 24 | 第二,很多语言有一个“全局解释器锁(GIL)”,它在很多情况下限制了并发。这在安全的名义下被使用,也有一定的积极影响,不过它限制了同时可以进行的工作的数量,这是一个很负面的影响。 25 | 26 | 为了强调这两方面,我们将创建一个大量使用这两方面的项目。因为这个例子关注的是将Rust嵌入到其他语言中,而不是问题自身,我们只使用一个玩具例子: 27 | 28 | > 启动10个线程。在每个线程中,从1数到500万。在所有10个线程结束后,打印“done”。 29 | 30 | 我选择 500 万基于我特定的电脑。这里是一个例子的 Ruby 代码: 31 | 32 | ```ruby 33 | threads = [] 34 | 35 | 10.times do 36 | threads << Thread.new do 37 | count = 0 38 | 39 | 5_000_000.times do 40 | count += 1 41 | end 42 | 43 | count 44 | end 45 | end 46 | 47 | threads.each do |t| 48 | puts "Thread finished with count=#{t.value}" 49 | end 50 | puts "done!" 51 | ``` 52 | 53 | 尝试运行这个例子,并选择一个将运行几秒钟的数字。基于你电脑的硬件配置,你可能需要增大或减小这个数字。 54 | 55 | 在我的系统中,运行这个例子花费`2.156`秒。并且,如果我用一些进程监视工具,像`top`,我可以看到它只用了我的机器的一个核。这是 GIL 在起作用。 56 | 57 | 虽然这确实是一个虚构的程序,你可以想象许多问题与现实世界中的问题相似。为了我们的目标,启动一些繁忙的线程来代表一些并行的,昂贵的计算。 58 | 59 | ## 一个Rust库 60 | 让我们用Rust重写这个问题。首先,让我们用Cargo创建一个新项目: 61 | 62 | ```bash 63 | $ cargo new embed 64 | $ cd embed 65 | ``` 66 | 67 | 这个程序在Rust中很好写: 68 | 69 | ```rust 70 | use std::thread; 71 | 72 | fn process() { 73 | let handles: Vec<_> = (0..10).map(|_| { 74 | thread::spawn(|| { 75 | let mut x = 0; 76 | for _ in 0..5_000_000 { 77 | x += 1 78 | } 79 | x 80 | }) 81 | }).collect(); 82 | 83 | for h in handles { 84 | println!("Thread finished with count={}", 85 | h.join().map_err(|_| "Could not join a thread!").unwrap()); 86 | } 87 | } 88 | ``` 89 | 90 | 一些代码可能与前面的例子类似。我们启动了 10个 线程,把它们收集到一个`handles`向量中。在每一个线程里,我们循环 500 万次,并每次给`_x`加一。最后,我们同步每个线程。 91 | 92 | 然而现在,这是一个 Rust 库,而且它并没有暴露任何可以从C中调用的东西。如果现在我们尝试在别的语言中链接这个库,这并不能工作。我们只需做两个小的改变来修复这个问题,第一个是修改我们代码的开头: 93 | 94 | ```rust 95 | #[no_mangle] 96 | pub extern fn process() { 97 | ``` 98 | 99 | 我们必须增加一个新的属性,`no_mangle`。当你创建了一个Rust库,编译器会在输出中修改函数的名称。这么做的原因超出了本教程的范围,不过为了其他语言能够知道如何调用这些函数,我们需要禁止这么做。这个属性将它关闭。 100 | 101 | 另一个变化是`pub extern`。`pub`意味着这个函数应当从模块外被调用,而`extern`说它应当能被C调用。这就是了!没有更多的修改。 102 | 103 | 我们需要做的第二件事是修改我们的`Cargo.toml`的一个设定。在底部加上这些: 104 | 105 | ```toml 106 | [lib] 107 | name = "embed" 108 | crate-type = ["dylib"] 109 | ``` 110 | 111 | 这告诉Rust我们想要将我们的库编译为一个标准的动态库。默认,Rust 编译为一个“rlib”,一个 Rust 特定的格式。 112 | 113 | 现在让我们构建这个项目: 114 | 115 | ```bash 116 | $ cargo build --release 117 | Compiling embed v0.1.0 (file:///home/steve/src/embed) 118 | ``` 119 | 120 | 我们选择了`cargo build --release`,它打开了优化进行构建。我们想让它越快越好!你可以在`target/release`找到输出的库: 121 | 122 | ```bash 123 | $ ls target/release/ 124 | build deps examples libembed.so native 125 | ``` 126 | 127 | 那个`libembed.so`就是我们的“共享目标(动态)”库。我们可以像任何用C写的动态库使用这个文件!另外,这也可能有`embed.dll` (Microsoft Windows) 或`libembed.dylib` (Mac OS X),基于不同的平台。 128 | 129 | 现在我们构建了我们的 Rust 库,让我们在 Ruby 中使用它。 130 | 131 | ## Ruby 132 | 在我们的项目中打开一个`embed.rb`文件。并这么做: 133 | 134 | ```ruby 135 | require 'ffi' 136 | 137 | module Hello 138 | extend FFI::Library 139 | ffi_lib 'target/release/libembed.so' 140 | attach_function :process, [], :void 141 | end 142 | 143 | Hello.process 144 | 145 | puts 'done!' 146 | ``` 147 | 148 | 在我们可以运行之前,我们需要安装`ffi`gem: 149 | 150 | ```bash 151 | $ gem install ffi # this may need sudo 152 | Fetching: ffi-1.9.8.gem (100%) 153 | Building native extensions. This could take a while... 154 | Successfully installed ffi-1.9.8 155 | Parsing documentation for ffi-1.9.8 156 | Installing ri documentation for ffi-1.9.8 157 | Done installing documentation for ffi after 0 seconds 158 | 1 gem installed 159 | ``` 160 | 161 | 最后,我们可以尝试运行它: 162 | 163 | ```bash 164 | $ ruby embed.rb 165 | Thread finished with count=5000000 166 | Thread finished with count=5000000 167 | Thread finished with count=5000000 168 | Thread finished with count=5000000 169 | Thread finished with count=5000000 170 | Thread finished with count=5000000 171 | Thread finished with count=5000000 172 | Thread finished with count=5000000 173 | Thread finished with count=5000000 174 | Thread finished with count=5000000 175 | done! 176 | done! 177 | $ 178 | ``` 179 | 180 | 哇哦,这很快欸!在我系统中,它花费了`0.086`秒,而不是纯 Ruby 所需的 2 秒。让我们分析下我们的 Ruby 代码: 181 | 182 | ```ruby 183 | require 'ffi' 184 | ``` 185 | 186 | 首先我们需要`ffi`gem。这让我们可以像 C 库一样使用 Rust 的接口。 187 | 188 | ```ruby 189 | module Hello 190 | extend FFI::Library 191 | ffi_lib 'target/release/libembed.so' 192 | ``` 193 | 194 | `Hello`模块被用来从共享库中附加原生函数。在其中,我们`extend`必要的`FFI::Library`模块,接着调用`ffi_lib`加载我们的动态库。我们仅仅传递我们库存储的路径,它是我们之前见过的,是`target/release/libembed.so`。 195 | 196 | ```ruby 197 | attach_function :process, [], :void 198 | ``` 199 | 200 | `attach_function`方法由`ffi`gem提供。它用来把我们Rust中`process()`连接到Ruby中同名函数。因为`process()`没有参数,第二个参数是一个空数组,并且因为它也没有返回值,我传递`:void`作为最后的参数。 201 | 202 | ```ruby 203 | Hello.process 204 | ``` 205 | 206 | 这是实际的Rust调用。我们的`module`和`attach_function`调用的组合设置了环境。它看起来像一个Ruby函数,不过它实际是Rust! 207 | 208 | ```ruby 209 | puts 'done!' 210 | ``` 211 | 212 | 最后,作为我们每个项目的要求,我们打印`done!`。 213 | 214 | 这就是全部!就像我们看到的,连接两个语言真是很简单,并为我们带来了很多性提升。 215 | 216 | 接下来,让我们试试 Python! 217 | 218 | ## Python 219 | 在这个目录中创建一个`embed.py`,并写入这些: 220 | 221 | ```python 222 | from ctypes import cdll 223 | 224 | lib = cdll.LoadLibrary("target/release/libembed.so") 225 | 226 | lib.process() 227 | 228 | print("done!") 229 | ``` 230 | 231 | 甚至更简单了!我们使用`ctypes`模块的`cdll`。之后是一个快速的`LoadLibrary`,然后我可以调用`process()`。 232 | 233 | 在我的系统,这花费了`0.017`秒。非常快! 234 | 235 | ## Node.js 236 | Node 并不是一个语言,不过目前它是服务端 JavaScript 居统治地位的实现。 237 | 238 | 为了在 Node 中进行 FFI,首先我们需要安装这个库: 239 | 240 | ```bash 241 | $ npm install ffi 242 | ``` 243 | 244 | 安装之后,我们就可以使用它了: 245 | 246 | ```javascript 247 | var ffi = require('ffi'); 248 | 249 | var lib = ffi.Library('target/release/libembed', { 250 | 'process': ['void', []] 251 | }); 252 | 253 | lib.process(); 254 | 255 | console.log("done!"); 256 | ``` 257 | 258 | 这看起来比 Python 的例子更像Ruby的例子。我们使用`ffi`模块来获取`ffi.Library()`,它加载我们的动态库。我们需要标明函数的返回值和参数值,它们是返回“void”,和一个空数组表明没有参数。这样,我们就可以调用它并打印结果。 259 | 260 | 在我的系统上,这会花费`0.092`秒,很快。 261 | 262 | ## 结论 263 | 如你所见,基础操作是**很**简单的。当然,这里有很多我们可以做的。查看[FFI](4.9.Foreign Function Interface 外部函数接口.md)章节以了解更多细节。 264 | -------------------------------------------------------------------------------- /content/Functions 函数.md: -------------------------------------------------------------------------------- 1 | # 函数 2 | 3 | > [functions.md](https://github.com/rust-lang/book/blob/master/first-edition/src/functions.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | 到目前为止你应该见过一个函数,`main`函数: 8 | 9 | ```rust 10 | fn main() { 11 | } 12 | ``` 13 | 14 | 这可能是最简单的函数声明。就像我们之前提到的,`fn`表示“这是一个函数”,后面跟着名字,一对括号因为这函数没有参数,然后是一对大括号代表函数体。下面是一个叫`foo`的函数: 15 | 16 | ```rust 17 | fn foo() { 18 | } 19 | ``` 20 | 21 | 那么有参数是什么样的呢?下面这个函数打印一个数字: 22 | 23 | ```rust 24 | fn print_number(x: i32) { 25 | println!("x is: {}", x); 26 | } 27 | ``` 28 | 29 | 下面是一个使用了`print_number`函数的完整的程序: 30 | 31 | ```rust 32 | fn main() { 33 | print_number(5); 34 | } 35 | 36 | fn print_number(x: i32) { 37 | println!("x is: {}", x); 38 | } 39 | ``` 40 | 41 | 如你所见,函数参数与`let`声明非常相似:参数名加上冒号再加上参数类型。 42 | 43 | 下面是一个完整的程序,它将两个数相加并打印结果: 44 | 45 | ```rust 46 | fn main() { 47 | print_sum(5, 6); 48 | } 49 | 50 | fn print_sum(x: i32, y: i32) { 51 | println!("sum is: {}", x + y); 52 | } 53 | ``` 54 | 55 | 在调用函数和声明函数时,你需要用逗号分隔多个参数。 56 | 57 | 与`let`不同,你**必须**为函数参数声明类型。下面代码将不能工作: 58 | 59 | ```rust 60 | fn print_sum(x, y) { 61 | println!("sum is: {}", x + y); 62 | } 63 | ``` 64 | 65 | 你会获得如下错误: 66 | 67 | ```text 68 | expected one of `!`, `:`, or `@`, found `)` 69 | fn print_sum(x, y) { 70 | ``` 71 | 72 | 这是一个有意为之的设计决定。即使像 Haskell 这样的能够全程序推断的语言,注明类型也经常作为一个最佳实践而被建议。我们认为即使允许在在函数体中推断,也要强制函数声明参数类型。这是一个全推断与无推断的最佳平衡。 73 | 74 | 如果我们要一个返回值呢?下面这个函数给一个整数加一: 75 | 76 | ```rust 77 | fn add_one(x: i32) -> i32 { 78 | x + 1 79 | } 80 | ``` 81 | 82 | Rust 函数只能返回一个值,并且你需要在一个“箭头”后面声明类型,它是一个破折号(`-`)后跟一个大于号(`>`)。 83 | 84 | 注意这里并没有一个分号。如果你把它加上: 85 | 86 | ```rust 87 | fn add_one(x: i32) -> i32 { 88 | x + 1; 89 | } 90 | ``` 91 | 92 | 你将会得到一个错误: 93 | 94 | ```text 95 | error: not all control paths return a value 96 | fn add_one(x: i32) -> i32 { 97 | x + 1; 98 | } 99 | 100 | help: consider removing this semicolon: 101 | x + 1; 102 | ^ 103 | ``` 104 | 105 | 这揭露了关于 Rust 两个有趣的地方:它是一个基于表达式的语言,并且分号与其它基于“大括号和分号”的语言不同。这两个方面是相关的。 106 | 107 | ## 表达式 VS 语句 108 | 109 | Rust 主要是一个基于表达式的语言。只有两种语句,其它的一切都是表达式。 110 | 111 | 然而这又有什么区别呢?表达式返回一个值,而语句不是。这就是为什么这里我们以“不是所有控制路径都返回一个值”结束:`x + 1;`语句不返回一个值。Rust 中有两种类型的语句:“声明语句”和“表达式语句”。其余的一切是表达式。让我们先讨论下声明语句。 112 | 113 | 在一些语言中,变量绑定可以被写成一个表达式,不仅仅是语句。例如 Ruby: 114 | 115 | ```ruby 116 | x = y = 5 117 | ``` 118 | 119 | 然而,在 Rust 中,使用`let`引入一个绑定并**不是**一个表达式。下面的代码会产生一个编译时错误: 120 | 121 | ```rust 122 | let x = (let y = 5); // expected identifier, found keyword `let` 123 | ``` 124 | 125 | 编译器告诉我们这里它期望看到表达式的开头,而`let`只能开始一个语句,不是一个表达式。 126 | 127 | 注意赋值一个已经绑定过的变量(例如,`y = 5`)仍是一个表达式,即使它的(返回)值并不是特别有用。不像其它语言中赋值语句返回它赋的值(例如,前面例子中的`5`),在 Rust 中赋值的值是一个空的元组`()`: 128 | 129 | ```rust 130 | let mut y = 5; 131 | 132 | let x = (y = 6); // `x` has the value `()`, not `6`. 133 | ``` 134 | 135 | Rust中第二种语句是**表达式语句**。它的目的是把任何表达式变为语句。在实践环境中,Rust 语法期望语句后跟其它语句。这意味着你用分号来分隔各个表达式。这意味着Rust看起来很像大部分其它使用分号做为语句结尾的语言,并且你会看到分号出现在几乎每一行你看到的 Rust 代码。 136 | 137 | 那么我们说“几乎”的例外是神马呢?你已经见过它了,在这些代码中: 138 | 139 | ```rust 140 | fn add_one(x: i32) -> i32 { 141 | x + 1 142 | } 143 | ``` 144 | 145 | 我们的函数声称它返回一个`i32`,但是带上个分号,它就会返回一个`()`。Rust意识到这大概不是我们想要的,并在之前我们看到的错误中建议去掉分号。 146 | 147 | ## 提早返回(Early returns) 148 | 不过提早返回怎么破?Rust确实有这么一个关键字,`return`: 149 | 150 | ```rust 151 | fn foo(x: i32) -> i32 { 152 | return x; 153 | 154 | // we never run this code! 155 | x + 1 156 | } 157 | ``` 158 | 159 | 使用`return`作为函数的最后一行是可行的,不过被认为是一个糟糕的风格: 160 | 161 | ```rust 162 | fn foo(x: i32) -> i32 { 163 | return x + 1; 164 | } 165 | ``` 166 | 167 | 如果你之前没有使用过基于表达式的语言,那么前面的没有`return`的定义可能看起来有点奇怪。不过它随着时间的推移它会变得直观。 168 | 169 | ## 发散函数(Diverging functions) 170 | 171 | Rust有些特殊的语法叫“发散函数”,这些函数并不返回: 172 | 173 | ```rust 174 | fn diverges() -> ! { 175 | panic!("This function never returns!"); 176 | } 177 | ``` 178 | 179 | `panic!`是一个宏,类似我们已经见过的`println!()`。与`println!()`不同的是,`panic!()`导致当前的执行线程崩溃并返回指定的信息。因为这个函数会崩溃,所以它不会返回,所以它拥有一个类型`!`,它代表“发散”。 180 | 181 | 如果你添加一个叫做`diverges()`的函数并运行,你将会得到一些像这样的输出: 182 | 183 | ```text 184 | thread ‘main’ panicked at ‘This function never returns!’, hello.rs:2 185 | ``` 186 | 187 | 如果你想要更多信息,你可以设定`RUST_BACKTRACE`环境变量来获取 backtrace : 188 | 189 | ```bash 190 | $ RUST_BACKTRACE=1 ./diverges 191 | thread 'main' panicked at 'This function never returns!', hello.rs:2 192 | stack backtrace: 193 | 1: 0x7f402773a829 - sys::backtrace::write::h0942de78b6c02817K8r 194 | 2: 0x7f402773d7fc - panicking::on_panic::h3f23f9d0b5f4c91bu9w 195 | 3: 0x7f402773960e - rt::unwind::begin_unwind_inner::h2844b8c5e81e79558Bw 196 | 4: 0x7f4027738893 - rt::unwind::begin_unwind::h4375279447423903650 197 | 5: 0x7f4027738809 - diverges::h2266b4c4b850236beaa 198 | 6: 0x7f40277389e5 - main::h19bb1149c2f00ecfBaa 199 | 7: 0x7f402773f514 - rt::unwind::try::try_fn::h13186883479104382231 200 | 8: 0x7f402773d1d8 - __rust_try 201 | 9: 0x7f402773f201 - rt::lang_start::ha172a3ce74bb453aK5w 202 | 10: 0x7f4027738a19 - main 203 | 11: 0x7f402694ab44 - __libc_start_main 204 | 12: 0x7f40277386c8 - 205 | 13: 0x0 - 206 | ``` 207 | 208 | 如果你需要覆盖一个已经设置的`RUST_BACKTRACE`的值,同时你又不能仅仅 unset 这个变量,这时把它设置成`0`来避免获得 backtrace。任何其它(非 0 )值将打开 backtrace。 209 | 210 | ```text 211 | $ export RUST_BACKTRACE=1 212 | ... 213 | $ RUST_BACKTRACE=0 ./diverges 214 | thread '
' panicked at 'This function never returns!', hello.rs:2 215 | note: Run with `RUST_BACKTRACE=1` for a backtrace. 216 | ``` 217 | 218 | `RUST_BACKTRACE`也可以用于 Cargo 的`run`命令: 219 | 220 | ```bash 221 | $ RUST_BACKTRACE=1 cargo run 222 | Running `target/debug/diverges` 223 | thread '
' panicked at 'This function never returns!', hello.rs:2 224 | stack backtrace: 225 | 1: 0x7f402773a829 - sys::backtrace::write::h0942de78b6c02817K8r 226 | 2: 0x7f402773d7fc - panicking::on_panic::h3f23f9d0b5f4c91bu9w 227 | 3: 0x7f402773960e - rt::unwind::begin_unwind_inner::h2844b8c5e81e79558Bw 228 | 4: 0x7f4027738893 - rt::unwind::begin_unwind::h4375279447423903650 229 | 5: 0x7f4027738809 - diverges::h2266b4c4b850236beaa 230 | 6: 0x7f40277389e5 - main::h19bb1149c2f00ecfBaa 231 | 7: 0x7f402773f514 - rt::unwind::try::try_fn::h13186883479104382231 232 | 8: 0x7f402773d1d8 - __rust_try 233 | 9: 0x7f402773f201 - rt::lang_start::ha172a3ce74bb453aK5w 234 | 10: 0x7f4027738a19 - main 235 | 11: 0x7f402694ab44 - __libc_start_main 236 | 12: 0x7f40277386c8 - 237 | 13: 0x0 - 238 | ``` 239 | 240 | 发散函数可以被用作任何类型: 241 | 242 | ```rust 243 | # fn diverges() -> ! { 244 | # panic!("This function never returns!"); 245 | # } 246 | let x: i32 = diverges(); 247 | let x: String = diverges(); 248 | ``` 249 | 250 | ## 函数指针 251 | 252 | 我们也可以创建指向函数的变量绑定: 253 | 254 | ```rust 255 | let f: fn(i32) -> i32; 256 | ``` 257 | 258 | `f`是一个指向一个获取`i32`作为参数并返回`i32`的函数的变量绑定。例如: 259 | 260 | ```rust 261 | fn plus_one(i: i32) -> i32 { 262 | i + 1 263 | } 264 | 265 | // without type inference 266 | let f: fn(i32) -> i32 = plus_one; 267 | 268 | // with type inference 269 | let f = plus_one; 270 | ``` 271 | 272 | 你可以用`f`来调用这个函数: 273 | 274 | ```rust 275 | # fn plus_one(i: i32) -> i32 { i + 1 } 276 | # let f = plus_one; 277 | let six = f(5); 278 | ``` 279 | -------------------------------------------------------------------------------- /content/Iterators 迭代器.md: -------------------------------------------------------------------------------- 1 | # 迭代器 2 | 3 | > [iterators.md](https://github.com/rust-lang/book/blob/master/first-edition/src/iterators.md) 4 | >
5 | > commit 27602e47dbd24b1ce273e54a82eca32cc231794d 6 | 7 | 让我们讨论一下循环。 8 | 9 | 还记得 Rust 的`for`循环吗?这是一个例子: 10 | 11 | ```rust 12 | for x in 0..10 { 13 | println!("{}", x); 14 | } 15 | ``` 16 | 17 | 现在我们更加了解 Rust 了,我们可以谈谈这里的具体细节了。这个范围(`0..10`)是“迭代器”。我们可以重复调用迭代器的`.next()`方法,然后它会给我们一个数据序列。 18 | 19 | (另外,像`0..10`带有两个点号的 range 是包含左边(从 0 开始)但不包含右边的值(到 9 为止)。一个数学家会这么写“[0, 10)”。为了得到一个一个一直到 10 的 range 你可以写成`0...10`。) 20 | 21 | 就像这样: 22 | 23 | ```rust 24 | let mut range = 0..10; 25 | 26 | loop { 27 | match range.next() { 28 | Some(x) => { 29 | println!("{}", x); 30 | }, 31 | None => { break } 32 | } 33 | } 34 | ``` 35 | 36 | 我们创建了一个`range`的可变绑定,它是我们的迭代器。我们接着`loop`,它包含一个`match`。`match`用来匹配`range.next()`的结果,它给我们迭代器的下一个值。`next`返回一个`Option`,在这个例子中,如果有值,它会返回`Some(i32)`然后当我们循环完毕,就会返回`None`。如果我们得到`Some(i32)`,我们就会打印它,如果我们得到`None`,我们`break`出循环。 37 | 38 | 这个代码例子基本上和我们的`loop`版本一样。`for`只是`loop`/`match`/`break`结构的简便写法。 39 | 40 | 然而,`for`循环并不是唯一使用迭代器的结构。编写你自己的迭代器涉及到实现`Iterator`特性。然而特性不是本章教程的涉及范围,不过 Rust 提供了一系列的有用的迭代器帮助我们完成各种任务。但首先注意下**范围** 的一些局限性。 41 | 42 | **范围** 非常原始,我们通常可以用更好的替代方案。考虑下面的 Rust 反模式:用**范围** 来模拟 C-风格的`for`循环。比如你想遍历完 vector 的内容。你可能尝试这么写: 43 | 44 | ```rust 45 | let nums = vec![1, 2, 3]; 46 | 47 | for i in 0..nums.len() { 48 | println!("{}", nums[i]); 49 | } 50 | ``` 51 | 52 | 这严格的说比使用现成的迭代器还要糟。你可以直接在 vector 上遍历。所以这么写: 53 | 54 | ```rust 55 | let nums = vec![1, 2, 3]; 56 | 57 | for num in &nums { 58 | println!("{}", num); 59 | } 60 | ``` 61 | 62 | 这么写有两个原因。第一,它更明确的表明了我们的意图。我们迭代整个向量,而不是先迭代向量的索引,再按索引取值。第二,这个版本也更有效率:第一个版本会进行额外的边界检查因为它使用了索引,`nums[i]`。因为我们利用迭代器获取每个向量元素的引用,第二个例子中并没有边界检查。这在迭代器中非常常见:我们可以忽略不必要的边界检查,不过仍然知道我们是安全的。 63 | 64 | 这里还有一个细节不是 100% 清楚的就是`println!`是如何工作的。`num`是`&i32`类型。也就是说,它是一个`i32`的引用,并不是`i32`本身。`println!`为我们处理了解引用,所以我们并没有看到它。下面的代码也能工作: 65 | 66 | ```rust 67 | let nums = vec![1, 2, 3]; 68 | 69 | for num in &nums { 70 | println!("{}", *num); 71 | } 72 | ``` 73 | 74 | 现在我们显式的解引用了`num`。为什么`&nums`会给我们一个引用呢?首先,因为我们显式的使用了`&`。其二,如果它给我们数据,我们就是它的所有者了,这会涉及到生成数据的拷贝然后返回给我们拷贝。通过引用,我们只是借用了一个数据的引用,所以仅仅是传递了一个引用,并不涉及数据的移动。 75 | 76 | 那么,既然现在我们已经明确了范围通常不是我们需要的,那么让我们来讨论下你真正需要什么。 77 | 78 | 这里涉及到大体上相关的3类事物:迭代器,**迭代适配器**(*iterator adapters*)和**消费者**(*consumers*)。下面是一些定义: 79 | 80 | * **迭代器** 给你一个值的序列 81 | * **迭代适配器** 操作迭代器,产生一个不同输出序列的新迭代器 82 | * **消费者** 操作迭代器,产生最终值的集合 83 | 84 | 让我们先看看消费者,因为我们已经见过范围这个迭代器了。 85 | 86 | ## 消费者(Consumers) 87 | 88 | **消费者** 操作一个迭代器,返回一些值或者几种类型的值。最常见的消费者是`collect()`。这个代码还不能编译,不过它表明了我们的意图: 89 | 90 | ```rust 91 | let one_to_one_hundred = (1..101).collect(); 92 | ``` 93 | 94 | 如你所见,我们在迭代器上调用了`collect()`。`collect()`从迭代器中取得尽可能多的值,然后返回结果的集合。那么为什么这不能编译呢?因为Rust不能确定你想收集什么类型的值,所以你需要让它知道。下面是一个可以编译的版本: 95 | 96 | ```rust 97 | let one_to_one_hundred = (1..101).collect::>(); 98 | ``` 99 | 100 | 如果你还记得,`::<>`语法允许我们给出一个类型提示,所以我们可以告诉编译器我们需要一个整型的向量。但是你并不总是需要提供完整的类型。使用`_`可以让你提供一个部分的提示: 101 | 102 | ```rust 103 | let one_to_one_hundred = (1..101).collect::>(); 104 | ``` 105 | 106 | 这是指“请把值收集到`Vec`,不过自行推断`T`类型”。为此`_`有时被称为“类型占位符”。 107 | 108 | `collect()`是最常见的消费者,不过这还有其它的消费者。`find()`就是一个: 109 | 110 | ```rust 111 | let greater_than_forty_two = (0..100) 112 | .find(|x| *x > 42); 113 | 114 | match greater_than_forty_two { 115 | Some(_) => println!("Found a match!"), 116 | None => println!("No match found :("), 117 | } 118 | ``` 119 | 120 | `find`接收一个闭包,然后处理迭代器中每个元素的引用。如果这个元素是我们要找的,那么这个闭包返回`true`,如果不是就返回`false`。因为我们可能不能找到任何元素,所以`find`返回`Option`而不是元素本身。 121 | 122 | 另一个重要的消费者是`fold`。他看起来像这样: 123 | 124 | ```rust 125 | let sum = (1..4).fold(0, |sum, x| sum + x); 126 | ``` 127 | 128 | `fold()`看起来像这样:`fold(base, |accumulator, element| ...)`。它需要两个参数:第一个参数叫做**基数**(*base*)。第二个是一个闭包,它自己也需要两个参数:第一个叫做**累计数**(*accumulator*),第二个叫**元素**(*element*)。每次迭代,这个闭包都会被调用,返回值是下一次迭代的累计数。在我们的第一次迭代,累计数的值是基数。 129 | 130 | 好吧,这有点混乱。让我们检查一下这个迭代器中所有这些值: 131 | 132 | | 基数 | 累计数 | 元素 | 闭包结果 | 133 | |------|-------------|---------|----------------| 134 | | 0 | 0 | 1 | 1 | 135 | | 0 | 1 | 2 | 3 | 136 | | 0 | 3 | 3 | 6 | 137 | 138 | 我们可以使用这些参数调用`fold()`: 139 | 140 | ```rust 141 | # (1..4) 142 | .fold(0, |sum, x| sum + x); 143 | ``` 144 | 145 | 那么,`0`是我们的基数,`sum`是累计数,`x`是元素。在第一次迭代,我们设置`sum`为`0`,然后`x`是`nums`的第一个元素,`1`。我们接着把`sum`和`x`相加,得到`0 + 1 = 1`。在我们第二次迭代,`sum`成为我们的累计值,元素是数组的第二个值,`2`,`1 + 2 = 3`,然后它就是最后一次迭代的累计数。在这次迭代中,`x`是最后的元素,`3`,那么`3 + 3 = 6`,就是我们和的最终值。`1 + 2 + 3 = 6`,这就是我们的结果。 146 | 147 | 哇。最开始几次你见到`fold`的时候可能觉得有点奇怪,不过一旦你习惯了它,你就会在到处都用它。任何时候你有一个列表,然后你需要一个单一的结果,`fold`就是合适的。 148 | 149 | 消费者很重要还因为另一个我们没有讨论到的迭代器的属性:惰性。让我们更多的讨论一下迭代器,你就知道为什么`消费者`重要了。 150 | 151 | ## 迭代器(Iterators) 152 | 153 | 正如我们之前说的,迭代器是一个我们可以重复调用它的`.next()`方法,然后它会给我们一个数据序列的结构。因为你需要调用函数,这意味着迭代器是**惰性的**(*lazy *)并且不需要预先生成所有的值。例如,下面的代码并没有真正的生成`1-99`这些数,而是创建了一个值来代表这个序列: 154 | 155 | ```rust 156 | let nums = 1..100; 157 | ``` 158 | 159 | 因为我们没有用范围做任何事,它并未生成序列。让我们加上消费者: 160 | 161 | ```rust 162 | let nums = (1..100).collect::>(); 163 | ``` 164 | 165 | 现在,`collect()`会要求范围生成一些值,接着它会开始产生序列。 166 | 167 | 范围是你会见到的两个基本迭代器之一。另一个是`iter()`。`iter()`可以把一个向量转换为一个简单的按顺序给出每个值的迭代器: 168 | 169 | ```rust 170 | let nums = vec![1, 2, 3]; 171 | 172 | for num in nums.iter() { 173 | println!("{}", num); 174 | } 175 | ``` 176 | 177 | 这两个基本迭代器应该能胜任你的工作。还有一些高级迭代器,包括一个是无限的。 178 | 179 | 关于迭代器的介绍足够了。迭代适配器是关于迭代器最后一个要介绍的内容了。让我们开始吧! 180 | 181 | ## 迭代适配器(Iterator adapters) 182 | 183 | **迭代适配器**(*Iterator adapters*)获取一个迭代器然后按某种方法修改它,并产生一个新的迭代器。最简单的是一个是`map`: 184 | 185 | ```rust 186 | (1..100).map(|x| x + 1); 187 | ``` 188 | 189 | 在其他迭代器上调用`map`,然后产生一个新的迭代器,它的每个元素引用被调用了作为参数的闭包。所以它会给我们`2-100`这些数字。好吧,看起来是这样。如果你编译这个例子,你会得到一个警告: 190 | 191 | ```text 192 | warning: unused result which must be used: iterator adaptors are lazy and 193 | do nothing unless consumed, #[warn(unused_must_use)] on by default 194 | (1..100).map(|x| x + 1); 195 | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 196 | ``` 197 | 198 | 又是惰性!那个闭包永远也不会执行。这个例子也不会打印任何数字: 199 | 200 | ```rust 201 | (1..100).map(|x| println!("{}", x)); 202 | ``` 203 | 204 | 如果你尝试在一个迭代器上执行带有副作用的闭包,不如直接使用`for`。 205 | 206 | 有大量有趣的迭代适配器。`take(n)`会返回一个源迭代器下`n`个元素的新迭代器,注意这对源迭代器没有副作用。让我们试试我们之前的无限迭代器,`count()`: 207 | 208 | ```rust 209 | for i in (1..).take(5) { 210 | println!("{}", i); 211 | } 212 | ``` 213 | 214 | 这会打印: 215 | 216 | ```text 217 | 1 218 | 2 219 | 3 220 | 4 221 | 5 222 | ``` 223 | 224 | `filter()`是一个带有一个闭包参数的适配器。这个闭包返回`true`或`false`。`filter()`返回的新迭代器只包含闭包返回`true`的元素: 225 | 226 | ```rust 227 | for i in (1..100).filter(|&x| x % 2 == 0) { 228 | println!("{}", i); 229 | } 230 | ``` 231 | 232 | 这会打印出 1 到 100 之间所有的偶数。(注意:不像`map`,传递给`filter`闭包传递了一个元素的引用而不是元素本身。这里定义的过滤器使用`&x`模式来提取整型。过滤器闭包传递的是一个引用因为它返回`true`或`false`而不是元素,所以过滤器的实现必须保持元素的所有权并传递给新创建的迭代器。) 233 | 234 | (注意因为`filter`并不消费它迭代的元素,它传递每个元素的引用,所以过滤器使用`&x`来提取其中的整型数据。) 235 | 236 | 你可以链式的调用所有三种结构:以一个迭代器开始,适配几次,然后处理结果。看看下面的: 237 | 238 | ```rust 239 | (1..) 240 | .filter(|&x| x % 2 == 0) 241 | .filter(|&x| x % 3 == 0) 242 | .take(5) 243 | .collect::>(); 244 | ``` 245 | 246 | 这会给你一个包含`6`,`12`,`18`,`24`和`30`的向量。 247 | 248 | 这只是一个迭代器、迭代适配器和消费者如何帮助你的小尝试。有很多非常实用的迭代器,当然你也可以编写你自己的迭代器。迭代器提供了一个安全、高效的处理所有类型列表的方法。最开始它们显得比较不寻常,不过如果你玩转了它们,你就会上瘾的。关于不同迭代器和消费者的列表,查看[迭代器模块文档](http://doc.rust-lang.org/std/iter/)。 249 | -------------------------------------------------------------------------------- /content/Patterns 模式.md: -------------------------------------------------------------------------------- 1 | # 模式 2 | 3 | > [patterns.md](https://github.com/rust-lang/book/blob/master/first-edition/src/patterns.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | 模式在Rust中十分常见。我们在[变量绑定](Variable Bindings 变量绑定.md),[匹配表达式](Match 匹配.md)和其它一些地方使用它们。让我们开始一个快速的关于模式可以干什么的教程! 8 | 9 | 快速回顾:你可以直接匹配常量,并且`_`作为“任何”类型: 10 | 11 | ```rust 12 | let x = 1; 13 | 14 | match x { 15 | 1 => println!("one"), 16 | 2 => println!("two"), 17 | 3 => println!("three"), 18 | _ => println!("anything"), 19 | } 20 | ``` 21 | 22 | 这会打印出`one`。 23 | 24 | 可以在任何分支创建值的绑定: 25 | 26 | ```rust 27 | let x = 1; 28 | 29 | match x { 30 | y => println!("x: {} y: {}", x, y), 31 | } 32 | ``` 33 | 34 | 这会打印出: 35 | 36 | ```text 37 | x: 1 y: 1 38 | ``` 39 | 40 | 注意在同一匹配块中同时拥有捕获全部的`_`和捕获全部的绑定会产生错误: 41 | 42 | ```rust 43 | let x = 1; 44 | 45 | match x { 46 | y => println!("x: {} y: {}", x, y), 47 | _ => println!("anything"), // this causes an error as it is unreachable 48 | } 49 | ``` 50 | 51 | 这里有一个模式的陷阱:就像任何引入一个新绑定的语句,他们会引入隐藏。例如: 52 | 53 | ```rust 54 | let x = 1; 55 | let c = 'c'; 56 | 57 | match c { 58 | x => println!("x: {} c: {}", x, c), 59 | } 60 | 61 | println!("x: {}", x) 62 | ``` 63 | 64 | 这会打印: 65 | 66 | ```text 67 | x: c c: c 68 | x: 1 69 | ``` 70 | 71 | 换句话说,`x =>`匹配到了模式并引入了一个叫做`x`的新绑定。这个新绑定的作用域是匹配分支并拥有`c`的值。注意匹配作用域外的`x`的值对内部的`x`的值并无影响。因为我们已经有了一个`x`,新的`x`隐藏了它。 72 | 73 | ## 多重模式(Multiple patterns) 74 | 75 | 你可以使用`|`匹配多个模式: 76 | 77 | ```rust 78 | let x = 1; 79 | 80 | match x { 81 | 1 | 2 => println!("one or two"), 82 | 3 => println!("three"), 83 | _ => println!("anything"), 84 | } 85 | ``` 86 | 87 | 这会输出`one or two`。 88 | 89 | ## 解构(Destructuring) 90 | 91 | 如果你有一个复合数据类型,例如一个[结构体](Structs 结构体.md),你可以在模式中解构它: 92 | 93 | ```rust 94 | struct Point { 95 | x: i32, 96 | y: i32, 97 | } 98 | 99 | let origin = Point { x: 0, y: 0 }; 100 | 101 | match origin { 102 | Point { x, y } => println!("({},{})", x, y), 103 | } 104 | ``` 105 | 106 | 我们可以用`:`来给出一个不同的名字: 107 | 108 | ```rust 109 | struct Point { 110 | x: i32, 111 | y: i32, 112 | } 113 | 114 | let origin = Point { x: 0, y: 0 }; 115 | 116 | match origin { 117 | Point { x: x1, y: y1 } => println!("({},{})", x1, y1), 118 | } 119 | ``` 120 | 121 | 如果你只关心部分值,我们不需要给它们都命名: 122 | 123 | ```rust 124 | struct Point { 125 | x: i32, 126 | y: i32, 127 | } 128 | 129 | let point = Point { x: 2, y: 3 }; 130 | 131 | match point { 132 | Point { x, .. } => println!("x is {}", x), 133 | } 134 | ``` 135 | 136 | 这会输出`x is 2`。 137 | 138 | 你可以对任何成员进行这样的匹配,不仅仅是第一个: 139 | 140 | ```rust 141 | struct Point { 142 | x: i32, 143 | y: i32, 144 | } 145 | 146 | let point = Point { x: 2, y: 3 }; 147 | 148 | match point { 149 | Point { y, .. } => println!("y is {}", y), 150 | } 151 | ``` 152 | 153 | 这会输出`y is 3`。 154 | 155 | 这种“解构”行为可以用在任何复合数据类型上,例如[元组](Primitive Types 原生类型.md#元组(tuples))和[枚举](Enums 枚举.md) 156 | 157 | ## 忽略绑定(Ignoring bindings) 158 | 159 | 你可以在模式中使用`_`来忽视它的类型和值。例如,这是一个`Result`的`match`: 160 | 161 | ```rust 162 | # let some_value: Result = Err("There was an error"); 163 | match some_value { 164 | Ok(value) => println!("got a value: {}", value), 165 | Err(_) => println!("an error occurred"), 166 | } 167 | ``` 168 | 169 | 在第一个分支,我们绑定了`Ok`变量中的值为`value`,不过在`Err`分支,我们用`_`来忽视特定的错误,而只是打印了一个通用的错误信息。 170 | 171 | `_`在任何创建绑定的模式中都有效。这在忽略一个大大结构体的部分字段时很有用: 172 | 173 | ```rust 174 | fn coordinate() -> (i32, i32, i32) { 175 | // Generate and return some sort of triple tuple. 176 | # (1, 2, 3) 177 | } 178 | 179 | let (x, _, z) = coordinate(); 180 | ``` 181 | 182 | 这里,我们绑定元组第一个和最后一个元素为`x`和`z`,不过省略了中间的元素。 183 | 184 | 值得注意的是,_ 一开始并不绑定值,这意味着值可能并没有被移动(这里涉及到 Move 和 Copy,应该就是说你不用它的话就不会 Move): 185 | 186 | ```rust 187 | let tuple: (u32, String) = (5, String::from("five")); 188 | 189 | // Here, tuple is moved, because the String moved: 190 | let (x, _s) = tuple; 191 | 192 | // The next line would give "error: use of partially moved value: `tuple`". 193 | // println!("Tuple is: {:?}", tuple); 194 | 195 | // However, 196 | 197 | let tuple = (5, String::from("five")); 198 | 199 | // Here, tuple is _not_ moved, as the String was never moved, and u32 is Copy: 200 | let (x, _) = tuple; 201 | 202 | // That means this works: 203 | println!("Tuple is: {:?}", tuple); 204 | ``` 205 | 206 | 这也意味着任何临时变量将会在语句结束时立刻被释放掉: 207 | 208 | ```rust 209 | // Here, the String created will be dropped immediately, as it’s not bound: 210 | 211 | let _ = String::from(" hello ").trim(); 212 | ``` 213 | 214 | 你也可以在模式中用`..`来忽略多个值。 215 | 216 | ```rust 217 | enum OptionalTuple { 218 | Value(i32, i32, i32), 219 | Missing, 220 | } 221 | 222 | let x = OptionalTuple::Value(5, -2, 3); 223 | 224 | match x { 225 | OptionalTuple::Value(..) => println!("Got a tuple!"), 226 | OptionalTuple::Missing => println!("No such luck."), 227 | } 228 | ``` 229 | 230 | 这会打印`Got a tuple!`。 231 | 232 | ## `ref`和`ref mut` 233 | 234 | 如果你想要一个引用,使用`ref`关键字: 235 | 236 | ```rust 237 | let x = 5; 238 | 239 | match x { 240 | ref r => println!("Got a reference to {}", r), 241 | } 242 | ``` 243 | 244 | 这会输出`Got a reference to 5`。 245 | 246 | 这里,`match`中的`r`是`&i32`类型的。换句话说,`ref`关键字创建了一个在模式中使用的引用。如果你需要一个可变引用,`ref mut`同样可以做到: 247 | 248 | ```rust 249 | let mut x = 5; 250 | 251 | match x { 252 | ref mut mr => println!("Got a mutable reference to {}", mr), 253 | } 254 | ``` 255 | 256 | ## 范围(Ranges) 257 | 258 | 你可以用`...`匹配一个范围的值: 259 | 260 | ```rust 261 | let x = 1; 262 | 263 | match x { 264 | 1 ... 5 => println!("one through five"), 265 | _ => println!("anything"), 266 | } 267 | ``` 268 | 269 | 这会输出`one through five`。 270 | 271 | 范围经常用在整数和`char`上。 272 | 273 | ```rust 274 | let x = '💅'; 275 | 276 | match x { 277 | 'a' ... 'j' => println!("early letter"), 278 | 'k' ... 'z' => println!("late letter"), 279 | _ => println!("something else"), 280 | } 281 | ``` 282 | 283 | 这会输出`something else`。 284 | 285 | ## 绑定 286 | 287 | 你可以使用`@`把值绑定到名字上: 288 | 289 | ```rust 290 | let x = 1; 291 | 292 | match x { 293 | e @ 1 ... 5 => println!("got a range element {}", e), 294 | _ => println!("anything"), 295 | } 296 | ``` 297 | 298 | 这会输出`got a range element 1`。在你想对一个复杂数据结构进行部分匹配的时候,这个特性十分有用: 299 | 300 | ```rust 301 | #[derive(Debug)] 302 | struct Person { 303 | name: Option, 304 | } 305 | 306 | let name = "Steve".to_string(); 307 | let x: Option = Some(Person { name: Some(name) }); 308 | match x { 309 | Some(Person { name: ref a @ Some(_), .. }) => println!("{:?}", a), 310 | _ => {} 311 | } 312 | ``` 313 | 314 | 这会输出 `Some("Steve")`,因为我们把Person里面的`name`绑定到`a`。 315 | 316 | 如果你在使用`|`的同时也使用了`@`,你需要确保名字在每个模式的每一部分都绑定名字: 317 | 318 | ```rust 319 | let x = 5; 320 | 321 | match x { 322 | e @ 1 ... 5 | e @ 8 ... 10 => println!("got a range element {}", e), 323 | _ => println!("anything"), 324 | } 325 | ``` 326 | 327 | ## 守卫(Guards) 328 | 329 | 你可以用`if`来引入**匹配守卫**(*match guards*): 330 | 331 | ```rust 332 | enum OptionalInt { 333 | Value(i32), 334 | Missing, 335 | } 336 | 337 | let x = OptionalInt::Value(5); 338 | 339 | match x { 340 | OptionalInt::Value(i) if i > 5 => println!("Got an int bigger than five!"), 341 | OptionalInt::Value(..) => println!("Got an int!"), 342 | OptionalInt::Missing => println!("No such luck."), 343 | } 344 | ``` 345 | 346 | 这会输出`Got an int!`。 347 | 348 | 如果你在`if`中使用多重模式,`if`条件将适用于所有模式: 349 | 350 | ```rust 351 | let x = 4; 352 | let y = false; 353 | 354 | match x { 355 | 4 | 5 if y => println!("yes"), 356 | _ => println!("no"), 357 | } 358 | ``` 359 | 360 | 这会打印`no`,因为`if`适用于整个` 4 | 5`,而不仅仅是`5`,换句话说,`if`语句的优先级是这样的: 361 | 362 | ```text 363 | (4 | 5) if y => ... 364 | ``` 365 | 366 | 而不是这样: 367 | 368 | ```text 369 | 4 | (5 if y) => ... 370 | ``` 371 | 372 | ## 混合与匹配(Mix and Match) 373 | 374 | (口哨)!根据你的需求,你可以对上面的多种匹配方法进行组合: 375 | 376 | ```rust 377 | match x { 378 | Foo { x: Some(ref name), y: None } => ... 379 | } 380 | ``` 381 | 382 | 模式十分强大。好好使用它们。 383 | -------------------------------------------------------------------------------- /content/Trait Objects trait 对象.md: -------------------------------------------------------------------------------- 1 | # trait对象 2 | 3 | > [trait-objects.md](https://github.com/rust-lang/book/blob/master/first-edition/src/trait-objects.md) 4 | >
5 | > commit 23a7a7bdb6a6a43cd7efdd9176b1d3f75d9d0e70 6 | 7 | 当涉及到多态的代码时,我们需要一个机制来决定哪个具体的版本应该得到执行。这叫做“分发”(dispatch)。大体上有两种形式的分发:静态分发和动态分发。虽然 Rust 喜欢静态分发,不过它也提供了一个叫做“trait 对象”的机制来支持动态分发。 8 | 9 | ## 背景 10 | 11 | 在本章接下来的内容中,我们需要一个 trait 和一些实现。让我们来创建一个简单的`Foo`。它有一个返回`String`的方法。 12 | 13 | ```rust 14 | trait Foo { 15 | fn method(&self) -> String; 16 | } 17 | ``` 18 | 19 | 我们也在`u8`和`String`上实现了这个 trait: 20 | 21 | ```rust 22 | # trait Foo { fn method(&self) -> String; } 23 | impl Foo for u8 { 24 | fn method(&self) -> String { format!("u8: {}", *self) } 25 | } 26 | 27 | impl Foo for String { 28 | fn method(&self) -> String { format!("string: {}", *self) } 29 | } 30 | ``` 31 | 32 | ## 静态分发 33 | 34 | 我们可以使用 trait 的限制来进行静态分发: 35 | 36 | ```rust 37 | # trait Foo { fn method(&self) -> String; } 38 | # impl Foo for u8 { fn method(&self) -> String { format!("u8: {}", *self) } } 39 | # impl Foo for String { fn method(&self) -> String { format!("string: {}", *self) } } 40 | fn do_something(x: T) { 41 | x.method(); 42 | } 43 | 44 | fn main() { 45 | let x = 5u8; 46 | let y = "Hello".to_string(); 47 | 48 | do_something(x); 49 | do_something(y); 50 | } 51 | ``` 52 | 53 | 在这里 Rust 用“单态”来进行静态分发。这意味着 Rust 会为`u8`和`String`分别创建一个特殊版本的的`do_something()`,然后将对`do_something`的调用替换为这些特殊函数。也就是说,Rust 生成了一些像这样的函数: 54 | 55 | ```rust 56 | # trait Foo { fn method(&self) -> String; } 57 | # impl Foo for u8 { fn method(&self) -> String { format!("u8: {}", *self) } } 58 | # impl Foo for String { fn method(&self) -> String { format!("string: {}", *self) } } 59 | fn do_something_u8(x: u8) { 60 | x.method(); 61 | } 62 | 63 | fn do_something_string(x: String) { 64 | x.method(); 65 | } 66 | 67 | fn main() { 68 | let x = 5u8; 69 | let y = "Hello".to_string(); 70 | 71 | do_something_u8(x); 72 | do_something_string(y); 73 | } 74 | ``` 75 | 76 | 这样做的一个很大的优点在于:静态分发允许函数被内联调用,因为调用者在编译时就知道它,内联对编译器进行代码优化十分有利。静态分发能提高程序的运行效率,不过相应的也有它的弊端:会导致“代码膨胀”(code bloat)。因为在编译出的二进制程序中,同样的函数,对于每个类型都会有不同的拷贝存在。 77 | 78 | 此外,编译器也不是完美的并且“优化”后的代码可能更慢。例如,过度的函数内联会导致指令缓存膨胀(缓存控制着我们周围的一切)。这也是为何要谨慎使用`#[inline]`和`#[inline(always)]`的部分原因。另外一个使用动态分发的原因是,在一些情况下,动态分发更有效率。 79 | 80 | 然而,常规情况下静态分发更有效率,并且我们总是可以写一个小的静态分发的封装函数来进行动态分发,不过反过来不行,这就是说静态调用更加灵活。因为这个原因标准库尽可能的使用了静态分发。 81 | 82 | ## 动态分发 83 | 84 | Rust 通过一个叫做“trait 对象”的功能提供动态分发。比如说`&Foo`、`Box`这些就是trait对象。它们是一些值,值中储存实现了特定 trait 的**任意**类型。它的具体类型只能在运行时才能确定。 85 | 86 | 从一些实现了特定`trait`的类型的指针中,可以从通过**转型**(casting)(例如,`&x as &Foo`)或者**强制转型**(coercing it)(例如,把`&x`当做参数传递给一个接收`&Foo`类型的函数)来取得trait对象。 87 | 88 | 这些 trait 对象的强制多态和转型也适用于类似于`&mut Foo`的`&mut T`以及`Box`的`Box`这样的指针,也就是目前为止我们讨论到的所有指针。强制转型和转型是一样的。 89 | 90 | 这个操作可以被看作“清除”编译器关于特定类型指针的信息,因此trait对象有时被称为“类型清除”(type erasure)。 91 | 92 | 回到上面的例子,我们可以使用相同的 trait,通过 trait 对象的转型(casting)来进行动态分发: 93 | 94 | ```rust 95 | # trait Foo { fn method(&self) -> String; } 96 | # impl Foo for u8 { fn method(&self) -> String { format!("u8: {}", *self) } } 97 | # impl Foo for String { fn method(&self) -> String { format!("string: {}", *self) } } 98 | 99 | fn do_something(x: &Foo) { 100 | x.method(); 101 | } 102 | 103 | fn main() { 104 | let x = 5u8; 105 | do_something(&x as &Foo); 106 | } 107 | ``` 108 | 109 | 或者通过强制转型(by concercing): 110 | 111 | ```rust 112 | # trait Foo { fn method(&self) -> String; } 113 | # impl Foo for u8 { fn method(&self) -> String { format!("u8: {}", *self) } } 114 | # impl Foo for String { fn method(&self) -> String { format!("string: {}", *self) } } 115 | 116 | fn do_something(x: &Foo) { 117 | x.method(); 118 | } 119 | 120 | fn main() { 121 | let x = "Hello".to_string(); 122 | do_something(&x); 123 | } 124 | ``` 125 | 126 | 一个使用trait对象的函数并没有为每个实现了`Foo`的类型专门生成函数:它只有一份函数的代码,一般(但不总是)会减少代码膨胀。然而,因为调用虚函数,会带来更大的运行时开销,也会大大地阻止任何内联以及相关优化的进行。 127 | 128 | ### 为什么用指针? 129 | 130 | 和很多托管语言不一样,Rust 默认不用指针来存放数据,因此类型有着不同的大小。在编译时知道值的大小(size),以及了解把值作为参数传递给函数、值在栈上移动、值在堆上分配(或释放)并储存等情况,对于 Rust 程序员来说是很重要的。 131 | 132 | 对于`Foo`,我们需要一个值至少是一个`String`(24字节)或一个`u8`(1字节),或者其它crate中可能实现了`Foo`(任意字节)的其他类型。如果值没有使用指针存储,我们无法保证代码能对其他类型正常运作,因为其它类型可以是任意大小的。 133 | 134 | 用指针来储存值意味着当我们使用 trait 对象时值的大小(size)是无关的,只与指针的大小(size)有关。 135 | 136 | ### 表现(Representation) 137 | 138 | 可以在一个 trait 对象上通过一个特殊的函数指针的记录调用的特性函数通常叫做“虚函数表”(由编译器创建和管理)。 139 | 140 | trait 对象既简单又复杂:它的核心表现和设计是十分直观的,不过这有一些难懂的错误信息和诡异行为有待发掘。 141 | 142 | 让我们从一个简单的,带有 trait 对象的运行时表现开始。`std::raw`模块包含与复杂的内建类型有相同结构的结构体,[包括trait对象](http://doc.rust-lang.org/std/raw/struct.TraitObject.html): 143 | 144 | ```rust 145 | # mod foo { 146 | pub struct TraitObject { 147 | pub data: *mut (), 148 | pub vtable: *mut (), 149 | } 150 | # } 151 | ``` 152 | 153 | 这就是了,一个trait对象就像包含一个“数据”指针和“虚函数表”指针的`&Foo`。 154 | 155 | 数据指针指向 trait 对象保存的数据(某个未知的类型`T`),和一个虚表指针指向对应`T`的`Foo`实现的虚函数表。 156 | 157 | 一个虚表本质上是一个函数指针的结构体,指向每个函数实现的具体机器码。一个像`trait_object.method()`的函数调用会从虚表中取出正确的指针然后进行一个动态调用。例如: 158 | 159 | ```rust 160 | struct FooVtable { 161 | destructor: fn(*mut ()), 162 | size: usize, 163 | align: usize, 164 | method: fn(*const ()) -> String, 165 | } 166 | 167 | // u8: 168 | 169 | fn call_method_on_u8(x: *const ()) -> String { 170 | // The compiler guarantees that this function is only called 171 | // with `x` pointing to a u8. 172 | let byte: &u8 = unsafe { &*(x as *const u8) }; 173 | 174 | byte.method() 175 | } 176 | 177 | static Foo_for_u8_vtable: FooVtable = FooVtable { 178 | destructor: /* compiler magic */, 179 | size: 1, 180 | align: 1, 181 | 182 | // Cast to a function pointer: 183 | method: call_method_on_u8 as fn(*const ()) -> String, 184 | }; 185 | 186 | 187 | // String: 188 | 189 | fn call_method_on_String(x: *const ()) -> String { 190 | // The compiler guarantees that this function is only called 191 | // with `x` pointing to a String. 192 | let string: &String = unsafe { &*(x as *const String) }; 193 | 194 | string.method() 195 | } 196 | 197 | static Foo_for_String_vtable: FooVtable = FooVtable { 198 | destructor: /* compiler magic */, 199 | // Values for a 64-bit computer, halve them for 32-bit ones 200 | size: 24, 201 | align: 8, 202 | 203 | method: call_method_on_String as fn(*const ()) -> String, 204 | }; 205 | ``` 206 | 207 | 在每个虚表中的`destructor`字段指向一个会清理虚表类型的任何资源的函数,对于`u8`是普通的,不过对于`String`它会释放内存。这对于像`Box`这类有所有权的trait对象来说是必要的,它需要在离开作用域后清理`Box`以及它内部的类型所分配的。`size`和`align`字段储存需要清除类型的大小和它的对齐需求。 208 | 209 | 假设我们有一些实现了`Foo`的值,那么显式的创建和使用`Foo`trait对象可能看起来有点像这个(忽略不匹配的类型,它们只是指针而已): 210 | 211 | ```rust 212 | let a: String = "foo".to_string(); 213 | let x: u8 = 1; 214 | 215 | // let b: &Foo = &a; 216 | let b = TraitObject { 217 | // Store the data: 218 | data: &a, 219 | // Store the methods: 220 | vtable: &Foo_for_String_vtable 221 | }; 222 | 223 | // let y: &Foo = x; 224 | let y = TraitObject { 225 | // Store the data: 226 | data: &x, 227 | // Store the methods: 228 | vtable: &Foo_for_u8_vtable 229 | }; 230 | 231 | // b.method(); 232 | (b.vtable.method)(b.data); 233 | 234 | // y.method(); 235 | (y.vtable.method)(y.data); 236 | ``` 237 | 238 | ## 对象安全(Object Safety) 239 | 240 | 并不是所有 trait 都可以被用来作为一个 trait 对象。例如,vector 实现了`Clone`,不过如果我们尝试创建一个 trait 对象: 241 | 242 | ```rust 243 | let v = vec![1, 2, 3]; 244 | let o = &v as &Clone; 245 | ``` 246 | 247 | 我们得到一个错误: 248 | 249 | ```text 250 | error: cannot convert to a trait object because trait `core::clone::Clone` is not object-safe [E0038] 251 | let o = &v as &Clone; 252 | ^~ 253 | note: the trait cannot require that `Self : Sized` 254 | let o = &v as &Clone; 255 | ^~ 256 | ``` 257 | 258 | 错误表明`Clone`并不是“对象安全的(object-safe)”。只有对象安全的 trait 才能成为 trait 对象。一个对象安全的 trait 需要如下两条为真: 259 | 260 | * trait 并不要求`Self: Sized` 261 | * 所有的方法是对象安全的 262 | 263 | 那么什么让一个方法是对象安全的呢?每一个方法必须要求`Self: Sized`或者如下所有: 264 | 265 | * 必须没有任何类型参数 266 | * 必须不使用`Self` 267 | 268 | 好的。如你所见,几乎所有的规则都谈到了`Self`。一个直观的理解是“除了特殊情况,如果你的 trait 的方法使用了`Self`,它就不是对象安全的”。 269 | -------------------------------------------------------------------------------- /content/Compiler Plugins 编译器插件.md: -------------------------------------------------------------------------------- 1 | # 编译器插件 2 | 3 | > [compiler-plugins.md](https://github.com/rust-lang/rust/blob/stable/src/doc/book/compiler-plugins.md) 4 | >
5 | > commit 28548db57d0acbc00ee80b43816953dbe31d53ba 6 | 7 | ## 介绍 8 | 9 | `rustc`可以加载编译器插件,它是由用户提供的库用来扩充编译器的行为,例如新的语法扩展,lint检查等。 10 | 11 | 一个插件是带有设计好的用来在`rustc`中注册扩展的**注册**(*registrar*)函数的一个动态库包装箱。其它包装箱可以使用`#![plugin(...)]`属性来装载这个扩展。查看[rustc::plugin](http://doc.rust-lang.org/rustc/plugin/)文档来获取更多关于定义和装载插件的机制。 12 | 13 | 如果属性存在的话,`#![plugin(foo(... args ...))]`传递的参数并不由`rustc`自身解释。它们被传递给插件的`Registry`[args方法](http://doc.rust-lang.org/rustc/plugin/registry/struct.Registry.html#method.args)。 14 | 15 | 在绝大多数情况中,一个插件应该**只**通过`#![plugin]`而不通过`extern crate`来使用。链接一个插件会将`libsyntax`和`librustc`加入到你的包装箱的依赖中。基本上你不会希望如此除非你在构建另一个插件。`plugin_as_library`lint会检查这些原则。 16 | 17 | 通常的做法是将插件放到它们自己的包装箱中,与任何那些会被库的调用者使用的`macro_rules!`宏或 Rust 代码分开。 18 | 19 | ## 语法扩展 20 | 21 | 插件可以有多种方法来扩展 Rust 的语法。一种语法扩展是宏过程。它们与[普通宏](5.35.Macros 宏.md)的调用方法一样,不过扩展是通过执行任意Rust代码在编译时操作[语法树](http://doc.rust-lang.org/syntax/ast/)进行的。 22 | 23 | 让我们写一个实现了罗马数字的插件[roman_numerals.rs]((https://github.com/rust-lang/rust/blob/master/src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs)。 24 | 25 | ```rust 26 | #![crate_type="dylib"] 27 | #![feature(plugin_registrar, rustc_private)] 28 | 29 | extern crate syntax; 30 | extern crate rustc; 31 | extern crate rustc_plugin; 32 | 33 | use syntax::parse::token; 34 | use syntax::tokenstream::TokenTree; 35 | use syntax::ext::base::{ExtCtxt, MacResult, DummyResult, MacEager}; 36 | use syntax::ext::build::AstBuilder; // A trait for expr_usize. 37 | use syntax::ext::quote::rt::Span; 38 | use rustc_plugin::Registry; 39 | 40 | fn expand_rn(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) 41 | -> Box { 42 | 43 | static NUMERALS: &'static [(&'static str, usize)] = &[ 44 | ("M", 1000), ("CM", 900), ("D", 500), ("CD", 400), 45 | ("C", 100), ("XC", 90), ("L", 50), ("XL", 40), 46 | ("X", 10), ("IX", 9), ("V", 5), ("IV", 4), 47 | ("I", 1)]; 48 | 49 | if args.len() != 1 { 50 | cx.span_err( 51 | sp, 52 | &format!("argument should be a single identifier, but got {} arguments", args.len())); 53 | return DummyResult::any(sp); 54 | } 55 | 56 | let text = match args[0] { 57 | TokenTree::Token(_, token::Ident(s)) => s.to_string(), 58 | _ => { 59 | cx.span_err(sp, "argument should be a single identifier"); 60 | return DummyResult::any(sp); 61 | } 62 | }; 63 | 64 | let mut text = &*text; 65 | let mut total = 0; 66 | while !text.is_empty() { 67 | match NUMERALS.iter().find(|&&(rn, _)| text.starts_with(rn)) { 68 | Some(&(rn, val)) => { 69 | total += val; 70 | text = &text[rn.len()..]; 71 | } 72 | None => { 73 | cx.span_err(sp, "invalid Roman numeral"); 74 | return DummyResult::any(sp); 75 | } 76 | } 77 | } 78 | 79 | MacEager::expr(cx.expr_usize(sp, total)) 80 | } 81 | 82 | #[plugin_registrar] 83 | pub fn plugin_registrar(reg: &mut Registry) { 84 | reg.register_macro("rn", expand_rn); 85 | } 86 | ``` 87 | 88 | 我们可以像其它宏那样使用`rn!()`: 89 | 90 | ```rust 91 | #![feature(plugin)] 92 | #![plugin(roman_numerals)] 93 | 94 | fn main() { 95 | assert_eq!(rn!(MMXV), 2015); 96 | } 97 | ``` 98 | 99 | 与一个简单的`fn(&str) -> u32`函数相比的优势有: 100 | 101 | * (任意复杂程度的)转换都发生在编译时 102 | * 输入验证也在编译时进行 103 | * 可以扩展并允许在模式中使用,它可以有效的为任何数据类型定义新语法。 104 | 105 | 除了宏过程,你可以定义新的类[derive](http://doc.rust-lang.org/reference.html#derive)属性和其它类型的扩展。查看[Registry::register_syntax_extension](http://doc.rust-lang.org/rustc/plugin/registry/struct.Registry.html#method.register_syntax_extension)和[SyntaxExtension enum](http://doc.rust-lang.org/syntax/ext/base/enum.SyntaxExtension.html)。对于更复杂的宏例子,查看[regex_macros](https://github.com/rust-lang/regex/blob/master/regex_macros/src/lib.rs)。 106 | 107 | ## 提示与技巧 108 | 109 | 这里提供一些[宏调试的提示](5.35.Macros 宏.md#debugging-macro-code)。 110 | 111 | 你可以使用[syntax::parse](http://doc.rust-lang.org/syntax/parse/)来将记号树转换为像表达式这样的更高级的语法元素: 112 | 113 | ```rust 114 | fn expand_foo(cx: &mut ExtCtxt, sp: Span, args: &[TokenTree]) 115 | -> Box { 116 | 117 | let mut parser = cx.new_parser_from_tts(args); 118 | 119 | let expr: P = parser.parse_expr(); 120 | ``` 121 | 122 | 看完[libsyntax解析器代码](https://github.com/rust-lang/rust/blob/master/src/libsyntax/parse/parser.rs)会给你一个解析基础设施如何工作的感觉。 123 | 124 | 保留你解析所有的[Span](http://doc.rust-lang.org/syntax/codemap/struct.Span.html),以便更好的报告错误。你可以用[Spanned](http://doc.rust-lang.org/syntax/codemap/struct.Spanned.html)包围你的自定数据结构。 125 | 126 | 调用[ExtCtxt::span_fatal](http://doc.rust-lang.org/syntax/ext/base/struct.ExtCtxt.html#method.span_fatal)将会立即终止编译。相反最好调用[ExtCtxt::span_err](http://doc.rust-lang.org/syntax/ext/base/struct.ExtCtxt.html#method.span_err)并返回[DummyResult](http://doc.rust-lang.org/syntax/ext/base/struct.DummyResult.html),这样编译器可以继续并找到更多错误。 127 | 128 | 为了打印用于调试的语法段,你可以同时使用[span_note](http://doc.rust-lang.org/syntax/ext/base/struct.ExtCtxt.html#method.span_note)和[syntax::print::pprust::*_to_string](http://doc.rust-lang.org/syntax/print/pprust/#functions)。 129 | 130 | 上面的例子使用[AstBuilder::expr_usize](http://doc.rust-lang.org/syntax/ext/build/trait.AstBuilder.html#tymethod.expr_usize)产生了一个普通整数。作为一个`AstBuilder`特性的额外选择,`libsyntax`提供了一个[准引用宏](http://doc.rust-lang.org/syntax/ext/quote/)的集合。它们并没有文档并且非常边缘化。然而,这些将会是实现一个作为一个普通插件库的改进准引用的好的出发点。 131 | 132 | ## Lint 插件 133 | 134 | 插件可以扩展[Rust Lint基础设施](http://doc.rust-lang.org/reference.html#lint-check-attributes)来添加额外的代码风格,安全检查等。你可以查看[src/test/auxiliary/lint_plugin_test.rs](https://github.com/rust-lang/rust/blob/master/src/test/run-pass-fulldeps/auxiliary/lint_plugin_test.rs)来了解一个完整的例子,我们在这里重现它的核心部分: 135 | 136 | ```rust 137 | #![feature(plugin_registrar)] 138 | #![feature(box_syntax, rustc_private)] 139 | 140 | extern crate syntax; 141 | 142 | // Load rustc as a plugin to get macros 143 | #[macro_use] 144 | extern crate rustc; 145 | extern crate rustc_plugin; 146 | 147 | use rustc::lint::{EarlyContext, LintContext, LintPass, EarlyLintPass, 148 | EarlyLintPassObject, LintArray}; 149 | use rustc_plugin::Registry; 150 | use syntax::ast; 151 | 152 | declare_lint!(TEST_LINT, Warn, "Warn about items named 'lintme'"); 153 | 154 | struct Pass; 155 | 156 | impl LintPass for Pass { 157 | fn get_lints(&self) -> LintArray { 158 | lint_array!(TEST_LINT) 159 | } 160 | } 161 | 162 | impl EarlyLintPass for Pass { 163 | fn check_item(&mut self, cx: &EarlyContext, it: &ast::Item) { 164 | if it.ident.name.as_str() == "lintme" { 165 | cx.span_lint(TEST_LINT, it.span, "item is named 'lintme'"); 166 | } 167 | } 168 | } 169 | 170 | #[plugin_registrar] 171 | pub fn plugin_registrar(reg: &mut Registry) { 172 | reg.register_early_lint_pass(box Pass as EarlyLintPassObject); 173 | } 174 | ``` 175 | 176 | 那么像这样的代码: 177 | 178 | ```rust 179 | #![plugin(lint_plugin_test)] 180 | 181 | fn lintme() { } 182 | ``` 183 | 184 | 将产生一个编译警告: 185 | 186 | ```text 187 | foo.rs:4:1: 4:16 warning: item is named 'lintme', #[warn(test_lint)] on by default 188 | foo.rs:4 fn lintme() { } 189 | ^~~~~~~~~~~~~~~ 190 | ``` 191 | 192 | Lint插件的组件有: 193 | 194 | * 一个或多个`declare_lint!`调用,它定义了[Lint](http://doc.rust-lang.org/rustc/lint/struct.Lint.html)结构 195 | * 一个用来存放lint检查所需的所有状态(在我们的例子中,没有) 196 | * 一个定义了如何检查每个语法元素的[LintPass](http://doc.rust-lang.org/rustc/lint/trait.LintPass.html)实现。一个单独的`LintPass`可能会对多个不同的`Lint`调用`span_lint`,不过它们都需要用`get_lints`方法进行注册。 197 | 198 | Lint过程是语法遍历,不过它们运行在编译的晚期,这时类型信息时可用的。`rustc`的[内建lint](https://github.com/rust-lang/rust/blob/master/src/librustc/lint/builtin.rs)与lint插件使用相同的基础构架,并提供了如何访问类型信息的例子。 199 | 200 | 由插件定义的语法通常通过[属性和插件标识](http://doc.rust-lang.org/reference.html#lint-check-attributes)控制,例如,`[#[allow(test_lint)]]`,`-A test-lint`。这些标识符来自于`declare_lint!`的第一个参数,经过合适的大小写和标点转换。 201 | 202 | 你可以运行`rustc -W help foo.rs`来见检查lint列表是否为`rustc`所知,包括由`foo.rs`加载的插件。 203 | -------------------------------------------------------------------------------- /content/Choosing your Guarantees 选择你的保证.md: -------------------------------------------------------------------------------- 1 | # 选择你的保证 2 | 3 | > [choosing-your-guarantees.md](https://github.com/rust-lang/book/blob/master/first-edition/src/choosing-your-guarantees.md) 4 | >
5 | > commit 23a7a7bdb6a6a43cd7efdd9176b1d3f75d9d0e70 6 | 7 | Rust 的一个重要特性是允许我们控制一个程序的开销和(安全)保证。 8 | 9 | Rust 标准库中有多种“wrapper 类型”的抽象,他们代表了大量在开销,工程学和安全保证之间的权衡。很多让你在运行时和编译时增强之间选择。这一部分将会详细解释一些特定的抽象。 10 | 11 | 在开始之前,强烈建议你阅读 Rust 的[所有权](Ownership 所有权.md)和[借用](References and Borrowing 引用和借用.md)。 12 | 13 | ## 基础指针类型 14 | 15 | ### `Box` 16 | 17 | [Box\](http://doc.rust-lang.org/stable/std/boxed/struct.Box.html)是一个“自我拥有的”,或者“装箱”的指针。因为它可以维持引用和包含的数据,它是数据的唯一的拥有者。特别的,当执行类似如下代码时: 18 | 19 | ```rust 20 | let x = Box::new(1); 21 | let y = x; 22 | // `x` is no longer accessible here. 23 | ``` 24 | 25 | 这里,装箱被**移动**进了`y`。因为`x`不再拥有它,此后编译器不再允许程序猿使用`x`。相似的一个函数可以通过返回装箱来**移出**函数。 26 | 27 | 当一个装箱(还没有被移动的)离开了作用域,析构函数将会运行。这个析构函数负责释放内部的数据。 28 | 29 | 这是一个动态分配的零开销抽象。如果你想要在堆上分配一些内存并安全的传递这些内存的指针,这是理想的情况。注意你将只能通过正常的借用规则来共享引用,这些在编译时被检查。 30 | 31 | ### `&T`和`&mut T` 32 | 33 | 这分别是不可变和可变引用。他们遵循“读写锁”的模式,也就是你只可能拥有一个数据的可变引用,或者任意数量的不可变引用,但不是两者都有。这个保证在编译时执行,并且没有明显的运行时开销。在大部分情况这两个指针类型有能力在代码块之间廉价的共享引用。 34 | 35 | 这些指针不能在超出他们的生命周期的情况下被拷贝。 36 | 37 | ### `*const T`和`*mut T` 38 | 39 | 这些是C风格的指针,并没附加生命周期或所有权。他们只是指向一些内存位置,没有其他的限制。他们能提供的唯一的保证是除非在标记为`unsafe`的代码中他们不会被解引用。 40 | 41 | 他们在构建像`Vec`这样的安全,低开销抽象时是有用的,不过应该避免在安全代码中使用。 42 | 43 | ### `Rc` 44 | 45 | 这是第一个我们将会介绍到的有运行时开销的包装类型。 46 | 47 | [Rc\](http://doc.rust-lang.org/stable/std/rc/struct.Rc.html)是一个引用计数指针。换句话说,这让我们拥有相同数据的多个“有所有权”的指针,并且数据在所有指针离开作用域后将被释放(析构函数将会执行)。 48 | 49 | 在内部,它包含一个共享的“引用计数”(也叫做“refcount”),每次`Rc`被拷贝时递增,而每次`Rc`离开作用域时递减。`Rc`的主要职责是确保共享的数据的析构函数被调用。 50 | 51 | 这里内部的数据是不可变的,并且如果创建了一个循环引用,数据将会泄露。如果我们想要数据在存在循环引用时不被泄漏,我们需要一个垃圾回收器。 52 | 53 | #### 保证 54 | 55 | 这里(`Rc`)提供的主要保证是,直到所有引用离开作用域后,相关数据才会被销毁。 56 | 57 | 当我们想要动态分配并在程序的不同部分共享一些(只读)数据,且不确定哪部分程序会最后使用这个指针时,我们应该用`Rc`。当`&T`不可能静态地检查正确性,或者程序员不想浪费开发时间编写反人类的代码时,它可以作为`&T`的可行的替代。 58 | 59 | 这个指针并**不是**线程安全的,并且Rust也不会允许它被传递或共享给别的线程。这允许你避免在不必要的情况下的原子性开销。 60 | 61 | `Rc`有个姐妹版智能指针类型——`Weak`。它是一个既没有所有权、也不能被借用的智能指针。它也比较像`&T`,但并没有生命周期的限制--一个`Weak`可以一直存活。然而,尝试对其内部数据进行访问可能失败并返回`None`,因为它可以比有所有权的`Rc`存活更久。这对循环数据结构和一些其他类型是有用的。 62 | 63 | #### 开销 64 | 65 | 随着内存使用增加,`Rc`是一次性的分配,虽然相比一个常规`Box`它会多分配额外两个字(也就是说,两个`usize`值)。(“强”引用计数相比“弱”引用计数)。 66 | 67 | `Rc`分别在拷贝和离开作用域时会产生递增/递减引用计数的计算型开销。注意拷贝将不会进行一次深度复制,相反它会简单的递增内部引用计数并返回一个`Rc`的拷贝。 68 | 69 | ## Cell 类型 70 | 71 | `Cell`提供内部可变性。换句话说,他们包含的数据可以被修改,即便是这个类型并不能以可变形式获取(例如,当他们位于一个`&`指针或`Rc`之后时)。 72 | 73 | [对此`cell`模块的文档有一个非常好的解释](http://doc.rust-lang.org/stable/std/cell/index.html)。 74 | 75 | 这些类型**经常**在结构体字段中出现,不过他们也可能在其他一些地方找到。 76 | 77 | ### `Cell` 78 | 79 | [Cell\](http://doc.rust-lang.org/stable/std/cell/struct.Cell.html)是一个提供了零开销内部可变性的类型,不过只用于`Copy`类型。因为编译器知道它包含的值对应的所有数据都位于栈上,所以并没有通过简单的替换数据而导致任何位于引用之后的数据泄露(或者更糟!)的担心。 80 | 81 | 然而使用这个封装仍有可能违反你自己的不可变性,所以谨慎的使用它。它是一个很好的标识,表明一些数据块是可变的并且可能在你第一次读取它和当你想要使用它时的值并不一样。 82 | 83 | ```rust 84 | use std::cell::Cell; 85 | 86 | let x = Cell::new(1); 87 | let y = &x; 88 | let z = &x; 89 | x.set(2); 90 | y.set(3); 91 | z.set(4); 92 | println!("{}", x.get()); 93 | ``` 94 | 95 | 注意这里我们可以通过多个不可变的引用改变相同的值。 96 | 97 | 这与如下代码有相同的运行时开销: 98 | 99 | ```rust 100 | let mut x = 1; 101 | let y = &mut x; 102 | let z = &mut x; 103 | x = 2; 104 | *y = 3; 105 | *z = 4; 106 | println!("{}", x); 107 | ``` 108 | 109 | 不过它有额外的优势,它确实能够编译成功。(高级黑?) 110 | 111 | #### 保证 112 | 113 | 这个类型放宽了当没有必要时“没有因可变性导致的混淆”的限制。然而,这也放宽了这个限制提供的保证;所以当你的不可变量依赖存储在`Cell`中的数据,你应该多加小心。 114 | 115 | 这对改变基本类型和其他`Copy`类型非常有用,当通过`&`和`&mut`的静态规则并没有其他简单合适的方法改变他们的值时。 116 | 117 | `Cell`并不让你获取数据的内部引用,它让我们可以自由改变值。 118 | 119 | #### 开销 120 | 121 | 使用`Cell`并没有运行时开销,不过你使用它来封装一个很大的(`Copy`)结构体,可能更适合封装单独的字段为`Cell`因为每次写入都会是一个结构体的完整拷贝。 122 | 123 | ### `RefCell` 124 | 125 | [RefCell\](http://doc.rust-lang.org/stable/std/cell/struct.RefCell.html)也提供了内部可变性,不过并不限制为`Copy`类型。 126 | 127 | 相对的,它有运行时开销。`RefCell`在运行时使用了读写锁模式,不像`&T`/`&mut T`那样在编译时执行。这通过`borrow()`和`borrow_mut()`函数来实现,它修改一个内部引用计数并分别返回可以不可变的和可变的解引用的智能指针。当智能指针离开作用域引用计数将被恢复。通过这个系统,我们可以动态的确保当有一个有效的可变借用时绝不会有任何其他有效的借用。如果程序猿尝试创建一个这样的借用,线程将会恐慌。 128 | 129 | ```rust 130 | use std::cell::RefCell; 131 | 132 | let x = RefCell::new(vec![1,2,3,4]); 133 | { 134 | println!("{:?}", *x.borrow()) 135 | } 136 | 137 | { 138 | let mut my_ref = x.borrow_mut(); 139 | my_ref.push(1); 140 | } 141 | ``` 142 | 143 | 与`Cell`相似,它主要用于难以或不可能满足借用检查的情况。大体上我们知道这样的改变不会发生在一个嵌套的形式中,不过检查一下是有好处的。 144 | 145 | 对于大型的,复杂的程序,把一些东西放入`RefCell`来将事情变简单是有用的。例如,Rust 编译器内部的[`ctxt`结构体](http://doc.rust-lang.org/stable/rustc/middle/ty/struct.ctxt.html)中的很多 map 都在这个封装中。他们只会在创建时被修改一次(但并不是正好在初始化后),或者在明显分开的地方多次多次修改。然而,因为这个结构体被广泛的用于各个地方,有效的组织可变和不可变的指针将会是困难的(也许是不可能的),并且可能产生大量的难以扩展的`&`指针。换句话说,`RefCell`提供了一个廉价(并不是零开销)的方式来访问它。之后,如果有人增加一些代码来尝试修改一个已经被借用的 cell 时,这将会产生(通常是决定性的)一个恐慌,并会被追溯到那个可恶的借用上。 146 | 147 | 相似的,在 Servo 的 DOM 中有很多可变量,大部分对于一个 DOM 类型都是本地的,不过有一些交错在 DOM 中并修改了很多内容。使用`RefCell`和`Cell`来保护所有的变化可以让我们免于担心到处都是的可变性,并且同时也表明了何处**正在**发生变化。 148 | 149 | 注意如果是一个能用`&`指针的非常简单的情形应该避免使用`RefCell`。 150 | 151 | #### 保证 152 | 153 | `RefCell`放宽了避免混淆的改变的**静态**限制,并代之以一个**动态**限制。保证本身并没有改变。 154 | 155 | #### 开销 156 | 157 | `RefCell`并不分配空间,不过它连同数据还包含一个额外的“借用状态”指示器(一个字的大小)。 158 | 159 | 在运行时每次借用产生一次引用计数的修改/检查。 160 | 161 | ## 同步类型(Synchronous types) 162 | 163 | 上面的很多类型不能以一种线程安全的方式使用。特别是`Rc`和`RefCell`,他们都使用非原子的引用计数(**原子**引用计数可以在不引起数据竞争的情况下在多个线程中递增),不能在多线程中使用。这让他们使用起来更廉价,不过我们也需要这两个类型的线程安全版本。他们以`Arc`和`Mutex`/`RWLock`的形式存在。 164 | 165 | 注意非线程安全的类型**不能**在线程间传递,并且这是在编译时检查的。 166 | 167 | ### `Arc` 168 | 169 | [Arc\](http://doc.rust-lang.org/stable/std/sync/struct.Arc.html)就是一个使用原子引用计数版本的`Rc`(*Atomic reference count*,因此是“Arc”)。它可以在线程间自由的传递。 170 | 171 | C++的`shared_ptr`与`Arc`类似,然而C++的情况中它的内部数据总是可以改变的。为了语义上与C++的形式相似,我们应该使用`Arc>`,`Arc>`,或者`Arc>`[^1]。最后一个应该只被用在我们能确定使用它并不会造成内存不安全性的情况下。记住写入一个结构体不是一个原子操作,并且很多像`vec.push()`这样的函数可以在内部重新分配内存并产生不安全的行为,所以即便是单一环境也不足以证明`UnsafeCell`是安全的。 172 | 173 | #### 保证 174 | 175 | 类似`Rc`,它提供了当最后的`Arc`离开作用域时(不包含任何的循环引用)其内部数据的析构函数将被执行的(线程安全的)保证。 176 | 177 | #### 开销 178 | 179 | 使用原子引用计数有额外的开销(无论是被拷贝或者离开作用域时都会发生)。当在一个单独的线程中通过一个`Arc`共享数据时,任何时候都更倾向于使用`&`指针。 180 | 181 | ### `Mutex`和`RwLock` 182 | 183 | [Mutex\](http://doc.rust-lang.org/stable/std/sync/struct.Mutex.html)和[RwLock\](http://doc.rust-lang.org/stable/std/sync/struct.RwLock.html)通过 RAII guard(guard 是一类直到析构函数被调用时能保持一些状态的对象)提供了互斥功能。对于这两个类型,mutex 直到我们调用`lock()`之前它都是无效的,此时直到我们获取锁这个线程都会被阻塞,同时它会返回一个 guard。这个 guard 可以被用来访问它的内部数据(可变的),而当 guard 离开作用域锁将被释放。 184 | 185 | ```rust 186 | { 187 | let guard = mutex.lock(); 188 | // `guard` dereferences mutably to the inner type. 189 | *guard += 1; 190 | } // Lock is released when destructor runs. 191 | ``` 192 | 193 | `RwLock`对多线程读有额外的效率优势。只要没有 writer,对于共享的数据总是可以安全的拥有多个 reader;同时`RwLock`让reader们获取一个“读取锁”。这样的锁可以并发的获取并通过引用计数记录。writer 必须获取一个“写入锁”,它只有在所有 reader 都离开作用域时才能获取。 194 | 195 | #### 保证 196 | 197 | 这两个类型都提供了线程间安全的共享可变性,然而他们易于产生死锁。一些额外的协议层次的安全性可以通过类型系统获取。 198 | 199 | #### 开销 200 | 201 | 他们在内部使用类原子类型来维持锁,这样的开销非常大(他们可以阻塞处理器所有的内存读取知道他们执行完毕)。而当有很多并发访问时等待这些锁也将是很慢的。 202 | 203 | ## 组合(Composition) 204 | 205 | 阅读 Rust 代码时的一个常见的痛苦之处是遇到形如`Rc>>`这样的类型(或者诸如此类的更复杂的组合)。这些组合式干什么的,和为什么作者会选这么一个类型(以及何时你应该在自己的代码中使用这样一个类型)的理由并不总是显而易见的。 206 | 207 | 通常,将你需要的保证组合到一起是一个例子,而不为无关紧要的东西产生开销。 208 | 209 | 例如,`Rc>`就是一个这样的组合。`Rc`自身并不能可变的解引用;因为`Rc`可以共享,而共享的可变性可以导致不安全的行为,所以我们在其中放入`RefCell`来获得可以动态验证的共享可变性。现在我们有了共享的可变数据,不过它只能以只有一个 writer(没有 reader)或多个 reader 的方式共享。 210 | 211 | 现在,我们可以更进一步,并拥有`Rc>>`或`Rc>>`,他们都是可共享可改变的vector,不过他们并不一样。 212 | 213 | 前者,`RefCell`封装了`Vec`,所以`Vec`整体是可变的。与此同时,同一时刻只能有一个整个`Vec`的可变借用。这意味着你的代码不能同时通过不同的`Rc`句柄来操作 vector 的不同元素。然而,我们可以随意的从`Vec`中加入或取出元素。这类似于一个有运行时借用检查的`&mut Vec`。 214 | 215 | 后者,借用作用于单独的元素,不过vector整体是不可变的。因此,我们可以独立的借用不同的元素,不过我们对vector加入或取出元素。这类似于`&mut [T]`[^2],不过同样会在运行时做借用检查。 216 | 217 | 在并发程序中,我们有一个使用`Arc>`的类似场景,它提供了共享可变性和所有权。 218 | 219 | 当阅读使用这些类型的代码时,一步步的阅读并关注他们提供的保证/开销。 220 | 221 | 当选择一个组合类型的时候,我们必须反过来思考;搞清楚我们需要何种保证,以及在组合中的何处我们需要他们。例如,如果面对一个`Vec>`和`RefCell>`之间的选择,我们需要明确像上面讲到的那样的权衡并选择其一。 222 | 223 | --- 224 | [^1]: `Arc>`实际上并不能编译因为`UnsafeCell`并不是`Send`或`Sync`的,不过我们可以把它 wrap 进一个类型并且手动为其实现`Send`/`Sync`来获得`Arc>`,它的`Wrapper`是`struct Wrapper(UnsafeCell)`。 225 | 226 | [^2]: `&[T]`和`&mut [T]`是**切片**(slice);他们包含一个指针和一个长度并可以引用一个vector或数组的一部分。`&mut [T]`能够改变它的元素,不过长度不能改变。 227 | -------------------------------------------------------------------------------- /content/References and Borrowing 引用和借用.md: -------------------------------------------------------------------------------- 1 | # 引用和借用 2 | 3 | > [references-and-borrowing.md](https://github.com/rust-lang/book/blob/master/first-edition/src/references-and-borrowing.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | 这篇教程是现行 3 个 Rust 所有权系统章节的第二部分。所有权系统是 Rust 最独特且最引人入胜的特性之一,也是作为 Rust 开发者应该熟悉的。Rust 所追求最大的目标 -- 内存安全,关键在于所有权。所有权系统有一些不同的概念,每个概念独自成章: 8 | 9 | * [所有权](5.8.Ownership 所有权.md),关键章节 10 | * 借用,你正在阅读的这个章节 11 | * [生命周期](5.10.Lifetimes 生命周期.md),关于借用的高级概念 12 | 13 | 这 3 章依次互相关联,你需要完整地阅读全部 3 章来对 Rust 的所有权系统进行全面的了解。 14 | 15 | ## 原则(Meta) 16 | 17 | 在我们开始详细讲解之前,这有两点关于所有权系统重要的注意事项。 18 | 19 | Rust 注重安全和速度。它通过很多**零开销抽象**(*zero-cost abstractions*)来实现这些目标,也就是说在 Rust 中,实现抽象的开销尽可能的小。所有权系统是一个典型的零开销抽象的例子。本文提到所有的分析都是**在编译时完成的**。你不需要在运行时为这些功能付出任何开销。 20 | 21 | 然而,这个系统确实有一个开销:学习曲线。很多 Rust 初学者会经历我们所谓的“与借用检查器作斗争”的过程,也就是指 Rust 编译器拒绝编译一个作者认为合理的程序。这种“斗争”会因为程序员关于所有权系统如何工作的基本模型与 Rust 实现的实际规则不匹配而经常发生。当你刚开始尝试 Rust 的时候,你很可能会有相似的经历。然而有一个好消息:更有经验的 Rust 开发者反映,一旦他们适应所有权系统一段时间之后,与借用检查器的冲突会越来越少。 22 | 23 | 记住这些之后,让我们来学习关于借用的内容。 24 | 25 | ## 借用 26 | 27 | 在[所有权](Ownership 所有权.md)章节的最后,我们有一个看起来像这样的糟糕的函数: 28 | 29 | ```rust 30 | fn foo(v1: Vec, v2: Vec) -> (Vec, Vec, i32) { 31 | // Do stuff with `v1` and `v2`. 32 | 33 | // Hand back ownership, and the result of our function. 34 | (v1, v2, 42) 35 | } 36 | 37 | let v1 = vec![1, 2, 3]; 38 | let v2 = vec![1, 2, 3]; 39 | 40 | let (v1, v2, answer) = foo(v1, v2); 41 | ``` 42 | 43 | 然而这并不是理想的 Rust 代码,因为它没有利用'借用'这个编程语言的特点。这是它的第一步: 44 | 45 | ```rust 46 | fn foo(v1: &Vec, v2: &Vec) -> i32 { 47 | // Do stuff with `v1` and `v2`. 48 | 49 | // Return the answer. 50 | 42 51 | } 52 | 53 | let v1 = vec![1, 2, 3]; 54 | let v2 = vec![1, 2, 3]; 55 | 56 | let answer = foo(&v1, &v2); 57 | 58 | // We can use `v1` and `v2` here! 59 | ``` 60 | 61 | 一个更具体的例子: 62 | 63 | ```rust 64 | fn main() { 65 | // Don't worry if you don't understand how `fold` works, the point here is that an immutable reference is borrowed. 66 | fn sum_vec(v: &Vec) -> i32 { 67 | return v.iter().fold(0, |a, &b| a + b); 68 | } 69 | // Borrow two vectors and sum them. 70 | // This kind of borrowing does not allow mutation through the borrowed reference. 71 | fn foo(v1: &Vec, v2: &Vec) -> i32 { 72 | // Do stuff with `v1` and `v2`. 73 | let s1 = sum_vec(v1); 74 | let s2 = sum_vec(v2); 75 | // Return the answer. 76 | s1 + s2 77 | } 78 | 79 | let v1 = vec![1, 2, 3]; 80 | let v2 = vec![4, 5, 6]; 81 | 82 | let answer = foo(&v1, &v2); 83 | println!("{}", answer); 84 | } 85 | ``` 86 | 87 | 与其获取`Vec`作为我们的参数,我们获取一个引用:`&Vec`。并与其直接传递`v1`和`v2`,我们传递`&v1`和`&v2`。我们称`&T`类型为一个”引用“,而与其拥有这个资源,它借用了所有权。一个借用变量的绑定在它离开作用域时并不释放资源。这意味着`foo()`调用之后,我们可以再次使用原始的绑定。 88 | 89 | 引用是不可变的,就像绑定一样。这意味着在`foo()`中,向量完全不能被改变: 90 | 91 | ```rust 92 | fn foo(v: &Vec) { 93 | v.push(5); 94 | } 95 | 96 | let v = vec![]; 97 | 98 | foo(&v); 99 | ``` 100 | 101 | 有如下错误: 102 | 103 | ```text 104 | error: cannot borrow immutable borrowed content `*v` as mutable 105 | v.push(5); 106 | ^ 107 | ``` 108 | 109 | 放入一个值改变了向量,所以我们不允许这样做 110 | 111 | ## `&mut`引用 112 | 113 | 这有第二种类型的引用:`&mut T`。一个“可变引用”允许你改变你借用的资源。例如: 114 | 115 | ```rust 116 | let mut x = 5; 117 | { 118 | let y = &mut x; 119 | *y += 1; 120 | } 121 | println!("{}", x); 122 | ``` 123 | 124 | 这会打印`6`。我们让`y`是一个`x`的可变引用,接着把`y`指向的值加一。你会注意到`x`也必须被标记为`mut`,如果它不是,我们不能获取一个不可变值的可变引用。 125 | 126 | 你也会发现我们在`y`前面加了一个星号(`*`),成了`*y`,这是因为`y`是一个`&mut`引用。你也需要使用他们(星号)来访问引用的内容。 127 | 128 | 否则,`&mut`引用就像一个普通引用。这两者之间,以及它们是如何交互的**有**巨大的区别。你会发现在上面的例子有些不太靠谱,因为我们需要额外的作用域,包围在`{`和`}`之间。如果我们移除它们,我们得到一个错误: 129 | 130 | ```text 131 | error: cannot borrow `x` as immutable because it is also borrowed as mutable 132 | println!("{}", x); 133 | ^ 134 | note: previous borrow of `x` occurs here; the mutable borrow prevents 135 | subsequent moves, borrows, or modification of `x` until the borrow ends 136 | let y = &mut x; 137 | ^ 138 | note: previous borrow ends here 139 | fn main() { 140 | 141 | } 142 | ^ 143 | ``` 144 | 145 | 正如这个例子表现的那样,有一些规则是你必须要掌握的。 146 | 147 | ## 规则 148 | 149 | Rust 中的借用有一些规则: 150 | 151 | 第一,任何借用必须位于比拥有者更小的作用域。第二,对于同一个资源(resource)的借用,以下情况不能同时出现在同一个作用域下: 152 | 153 | * 1 个或多个不可变引用(`&T`) 154 | * 唯一 1 个可变引用(`&mut T`) 155 | 156 | `译者注:即同一个作用域下,要么只有一个对资源 A 的可变引用(&mut T),要么有 N 个不可变引用(&T),但不能同时存在可变和不可变的引用` 157 | 158 | 你可能注意到这些看起来很眼熟,虽然并不完全一样,它类似于数据竞争的定义: 159 | 160 | > 当 2 个或更多个指针同时访问同一内存位置,当它们中至少有 1 个在写,同时操作并不是同步的时候存在一个“数据竞争” 161 | 162 | 通过引用,你可以拥有你想拥有的任意多的引用,因为它们没有一个在写。如果你在写,并且你需要2个或更多相同内存的指针,则你只能一次拥有一个`&mut`。这就是Rust如何在编译时避免数据竞争:如果打破规则的话,我们会得到错误。 163 | 164 | 在记住这些之后,让我们再次考虑我们的例子。 165 | 166 | ## 理解作用域(Thinking in scopes) 167 | 168 | 这是代码: 169 | 170 | ```rust 171 | fn main() { 172 | let mut x = 5; 173 | let y = &mut x; 174 | 175 | *y += 1; 176 | 177 | println!("{}", x); 178 | } 179 | ``` 180 | 181 | 这些代码给我们如下错误: 182 | 183 | ```text 184 | error: cannot borrow `x` as immutable because it is also borrowed as mutable 185 | println!("{}", x); 186 | ^ 187 | ``` 188 | 189 | 这是因为我们违反了规则:我们有一个指向`x`的`&mut T`,所以我们不允许创建任何`&T`。一个或另一个。错误记录提示了我们应该如何理解这个错误: 190 | 191 | ```text 192 | note: previous borrow ends here 193 | fn main() { 194 | 195 | } 196 | ^ 197 | ``` 198 | 199 | 换句话说,可变借用在剩下的例子中一直存在。我们需要的是可变借用在我们尝试调用`println!`**之前**结束并生成一个不可变借用。在 Rust 中,借用绑定在借用有效的作用域上。而我们的作用域看起来像这样: 200 | 201 | ```rust 202 | fn main() { 203 | let mut x = 5; 204 | 205 | let y = &mut x; // -+ &mut borrow of `x` starts here. 206 | // | 207 | *y += 1; // | 208 | // | 209 | println!("{}", x); // -+ - Try to borrow `x` here. 210 | } // -+ &mut borrow of `x` ends here. 211 | ``` 212 | 213 | 这些作用域冲突了:我们不能在`y`在作用域中时生成一个`&x`。 214 | 215 | 所以我们增加了一个大括号: 216 | 217 | ```rust 218 | let mut x = 5; 219 | 220 | { 221 | let y = &mut x; // -+ &mut borrow starts here. 222 | *y += 1; // | 223 | } // -+ ... and ends here. 224 | 225 | println!("{}", x); // <- Try to borrow `x` here. 226 | ``` 227 | 228 | 这就没有问题了。我们的可变借用在我们创建一个不可变引用之前离开了作用域。不过作用域是看清一个借用持续多久的关键。 229 | 230 | ## 借用避免的问题(Issues borrowing prevents) 231 | 232 | 为什么要有这些限制性规则?好吧,正如我们记录的,这些规则避免了数据竞争。数据竞争能造成何种问题呢?这里有一些。 233 | 234 | ### 迭代器失效(Iterator invalidation) 235 | 236 | 一个例子是“迭代器失效”,它在当你尝试改变你正在迭代的集合时发生。Rust 的借用检查器阻止了这些发生: 237 | 238 | ```rust 239 | let mut v = vec![1, 2, 3]; 240 | 241 | for i in &v { 242 | println!("{}", i); 243 | } 244 | ``` 245 | 246 | 这会打印出 1 到 3。因为我们在向量上迭代,我们只得到了元素的引用。同时`v`本身作为不可变借用,它意味着我们在迭代时不能改变它: 247 | 248 | ```rust 249 | let mut v = vec![1, 2, 3]; 250 | 251 | for i in &v { 252 | println!("{}", i); 253 | v.push(34); 254 | } 255 | ``` 256 | 257 | 这里是错误: 258 | 259 | ```text 260 | error: cannot borrow `v` as mutable because it is also borrowed as immutable 261 | v.push(34); 262 | ^ 263 | note: previous borrow of `v` occurs here; the immutable borrow prevents 264 | subsequent moves or mutable borrows of `v` until the borrow ends 265 | for i in &v { 266 | ^ 267 | note: previous borrow ends here 268 | for i in &v { 269 | println!(“{}”, i); 270 | v.push(34); 271 | } 272 | ^ 273 | ``` 274 | 275 | 我们不能修改`v`因为它被循环借用。 276 | 277 | ### 释放后使用 278 | 279 | 引用必须与它引用的值存活得一样长。Rust 会检查你的引用的作用域来保证这是正确的。 280 | 281 | 如果 Rust 并没有检查这个属性,我们可能意外的使用了一个无效的引用。例如: 282 | 283 | ```rust 284 | let y: &i32; 285 | { 286 | let x = 5; 287 | y = &x; 288 | } 289 | 290 | println!("{}", y); 291 | ``` 292 | 293 | 我们得到这个错误: 294 | 295 | ```text 296 | error: `x` does not live long enough 297 | y = &x; 298 | ^ 299 | note: reference must be valid for the block suffix following statement 0 at 300 | 2:16... 301 | let y: &i32; 302 | { 303 | let x = 5; 304 | y = &x; 305 | } 306 | 307 | note: ...but borrowed value is only valid for the block suffix following 308 | statement 0 at 4:18 309 | let x = 5; 310 | y = &x; 311 | } 312 | ``` 313 | 314 | 换句话说,`y`只在`x`存在的作用域中有效。一旦`x`消失,它变成无效的引用。为此,这个错误说借用“并没有存活得足够久”因为它在应该有效的时候是无效的。 315 | 316 | 当引用在它引用的变量**之前**声明会导致类似的问题。这是因为同一作用域中的资源以他们声明相反的顺序被释放: 317 | 318 | ```rust 319 | let y: &i32; 320 | let x = 5; 321 | y = &x; 322 | 323 | println!("{}", y); 324 | ``` 325 | 326 | 我们得到这个错误: 327 | 328 | ```text 329 | error: `x` does not live long enough 330 | y = &x; 331 | ^ 332 | note: reference must be valid for the block suffix following statement 0 at 333 | 2:16... 334 | let y: &i32; 335 | let x = 5; 336 | y = &x; 337 | 338 | println!("{}", y); 339 | } 340 | 341 | note: ...but borrowed value is only valid for the block suffix following 342 | statement 1 at 3:14 343 | let x = 5; 344 | y = &x; 345 | 346 | println!("{}", y); 347 | } 348 | ``` 349 | 350 | 在上面的例子中,`y`在`x`之前被声明,意味着`y`比`x`生命周期更长,这是不允许的。 351 | -------------------------------------------------------------------------------- /content/Lifetimes 生命周期.md: -------------------------------------------------------------------------------- 1 | # 生命周期 2 | 3 | > [lifetimes.md](https://github.com/rust-lang/book/blob/master/first-edition/src/lifetimes.md) 4 | >
5 | > commit ccb1d87d6faa9ff528d22b96595a0e2cbb16c0f2 6 | 7 | 这篇教程是现行 3 个 Rust 所有权系统章节的第三部分。所有权系统是 Rust 最独特且最引人入胜的特性之一,也是作为 Rust 开发者应该熟悉的。Rust 所追求最大的目标 -- 内存安全,关键在于所有权。所有权系统有一些不同的概念,每个概念独自成章: 8 | 9 | * [所有权](5.8.Ownership 所有权.md),关键章节 10 | * [借用](5.9.References and Borrowing 引用和借用.md),以及它关联的特性: "引用" (references) 11 | * 生命周期,你正在阅读的这个章节 12 | 13 | 这 3 章依次互相关联,你需要完整地阅读全部 3 章来对 Rust 的所有权系统进行全面的了解。 14 | 15 | ## 原则(Meta) 16 | 17 | 在我们开始详细讲解之前,这有两点关于所有权系统重要的注意事项。 18 | 19 | Rust 注重安全和速度。它通过很多**零开销抽象**(*zero-cost abstractions*)来实现这些目标,也就是说在 Rust 中,实现抽象的开销尽可能的小。所有权系统是一个典型的零开销抽象的例子。本文提到所有的分析都是**在编译时完成的**。你不需要在运行时为这些功能付出任何开销。 20 | 21 | 然而,这个系统确实有一个开销:学习曲线。很多 Rust 初学者会经历我们所谓的“与借用检查器作斗争”的过程,也就是指 Rust 编译器拒绝编译一个作者认为合理的程序。这种“斗争”会因为程序员关于所有权系统如何工作的基本模型与 Rust 实现的实际规则不匹配而经常发生。当你刚开始尝试 Rust 的时候,你很可能会有相似的经历。然而有一个好消息:更有经验的 Rust 开发者反映,一旦他们适应所有权系统一段时间之后,与借用检查器的冲突会越来越少。 22 | 23 | 记住这些之后,让我们来学习有关生命周期的内容。 24 | 25 | ## 生命周期 26 | 27 | 借出一个其它人所有资源的引用可以是很复杂的。例如,想象一下下列操作: 28 | 29 | 1. 我获取了一个某种资源的句柄 30 | 2. 我借给你了一个关于这个资源的引用 31 | 3. 我决定不再需要这个资源了,然后释放了它,这时你仍然持有它的引用 32 | 4. 你决定使用这个资源 33 | 34 | 噢!你的引用指向一个无效的资源。这叫做**悬垂指针**(*dangling pointer*)或者“释放后使用”,如果这个资源是内存的话。这种状况的一个小例子像这样: 35 | 36 | ```rust 37 | let r; // Introduce reference: `r`. 38 | { 39 | let i = 1; // Introduce scoped value: `i`. 40 | r = &i; // Store reference of `i` in `r`. 41 | } // `i` goes out of scope and is dropped. 42 | 43 | println!("{}", r); // `r` still refers to `i`. 44 | ``` 45 | 46 | 要修正这个问题的话,我们必须确保第四步永远也不在第三步之后发生。在上面的小例子中 Rust 编译器能够报告问题因为它能识别出函数中不同变量的生命周期。 47 | 48 | 当我们有一个将引用作为参数的函数时情况就变得更复杂了。考虑如下例子: 49 | 50 | ```rust 51 | fn skip_prefix(line: &str, prefix: &str) -> &str { 52 | // ... 53 | # line 54 | } 55 | 56 | let line = "lang:en=Hello World!"; 57 | let lang = "en"; 58 | 59 | let v; 60 | { 61 | let p = format!("lang:{}=", lang); // -+ `p` comes into scope. 62 | v = skip_prefix(line, p.as_str()); // | 63 | } // -+ `p` goes out of scope. 64 | println!("{}", v); 65 | ``` 66 | 67 | 这里我们有个函数`skip_prefix`,它获取两个`&str`引用作为参数并返回一个`&str`引用。通过`line`和`p`的引用调用它:两个有不同生命周期的变量。现在`println!`那行代码的安全依赖于`skip_prefix`函数返回的引用是仍然存在的`line`还是已经释放掉的`p`。 68 | 69 | 因为存在上述的歧义,Rust 将会拒绝编译示例代码。为了继续我们需要向编译器提供更多关于引用生命周期的信息。这可以通过再函数签名中显式标明生命周期来完成: 70 | 71 | ```rust 72 | fn skip_prefix<'a, 'b>(line: &'a str, prefix: &'b str) -> &'a str { 73 | // ... 74 | # line 75 | } 76 | ``` 77 | 78 | 让我们看看所做的修改,但是现在并不深入到语法--之后我们会讲到。第一个修改是再方法名后面加入了`<'a, 'b>`。这引入了两个生命周期参数`'a`和`'b`。接下来函数签名中的每个引用都关联了一个生命周期参数,通过再`&`之后加上生命周期的名字。这告诉了编译器不同引用的生命周期是如何关联的。 79 | 80 | 这样编译器就能推断出`skip_prefix`函数的返回值与`line`参数有着相同的生命周期,这样就使得之前例子中`v`引用即使在`p`离开作用域之后也能安全使用。 81 | 82 | 另外编译器能够检查`skip_prefix`返回值的用途,它也能确保之后的实现也遵守函数声明建立的约束。这在实现[之后会介绍到][traits]的 trait 时显得尤为实用。 83 | 84 | [traits]: Traits.md 85 | 86 | **注意:** 认识到生命周期注解是`_descriptive_`(描述性)而不是`_prescriptive_`(规定性)是很重要的(译者注:各位可以自行搜索这两个术语)。这意味着引用的生命周期是由代码决定的,而不是由生命周期注解决定的。注解,提供了供编译器用来检查引用的有效性的信息。编译器在简单的情况下无需注解就能进行这种检查,不过在复杂的场景需要程序猿的协助。 87 | 88 | ## 语法 89 | 90 | `'a`读作“生命周期 a”。技术上讲,每一个引用都有一些与之相关的生命周期,不过编译器在通常情况让你可以省略(也就是,省略,查看下面的[生命周期省略](#生命周期省略(lifetime-elision)))它们。在我们讲到它之前,让我们看看一个显式生命周期的例子: 91 | 92 | ```rust 93 | fn bar<'a>(...) 94 | ``` 95 | 96 | 之前我们讨论了一些[函数语法](Functions 函数.md),不过我们并没有讨论函数名后面的`<>`。一个函数可以在`<>`之间有“泛型参数”,生命周期也是其中一种。我们在[本书的后面](Generics 泛型.md)讨论其他类型的泛型。不过现在让我们着重看生命周期。 97 | 98 | 我们用`<>`声明了生命周期。这是说`bar`有一个生命周期`'a`。如果我们有两个拥有不同生命周期的引用参数,它应该看起来像这样: 99 | 100 | ```rust 101 | fn bar<'a, 'b>(...) 102 | ``` 103 | 104 | 接着在我们的参数列表中,我们使用了我们命名的生命周期: 105 | 106 | ```rust 107 | ...(x: &'a i32) 108 | ``` 109 | 110 | 如果我们想要一个`&mut`引用,我们这么做: 111 | 112 | ```rust 113 | ...(x: &'a mut i32) 114 | ``` 115 | 116 | 如果你对比一下`&mut i32`和`&'a mut i32`,他们是一样的,只是后者在`&`和`mut i32`之间夹了一个`'a`生命周期。`&mut i32`读作“一个`i32`的可变引用”,而`&'a mut i32`读作“一个带有生命周期'a的i32的可变引用”。 117 | 118 | ## 在`struct`中 119 | 120 | 当你处理[结构体](5.12.Structs 结构体.md)时你也需要显式的生命周期: 121 | 122 | ```rust 123 | struct Foo<'a> { 124 | x: &'a i32, 125 | } 126 | 127 | fn main() { 128 | let y = &5; // This is the same as `let _y = 5; let y = &_y;`. 129 | let f = Foo { x: y }; 130 | 131 | println!("{}", f.x); 132 | } 133 | ``` 134 | 135 | 如你所见,`struct`也可以有生命周期。跟函数类似的方法, 136 | 137 | ```rust 138 | struct Foo<'a> { 139 | # x: &'a i32, 140 | # } 141 | ``` 142 | 143 | 声明一个生命周期,接着 144 | 145 | ```rust 146 | # struct Foo<'a> { 147 | x: &'a i32, 148 | # } 149 | ``` 150 | 151 | 使用它。然而为什么这里我们需要一个生命周期呢?因为我们需要确保任何`Foo`的引用不能比它包含的`i32`的引用活的更久。 152 | 153 | ## `impl`块 154 | 155 | 让我们在`Foo`中实现一个方法: 156 | 157 | ```rust 158 | struct Foo<'a> { 159 | x: &'a i32, 160 | } 161 | 162 | impl<'a> Foo<'a> { 163 | fn x(&self) -> &'a i32 { self.x } 164 | } 165 | 166 | fn main() { 167 | let y = &5; // This is the same as `let _y = 5; let y = &_y;`. 168 | let f = Foo { x: y }; 169 | 170 | println!("x is: {}", f.x()); 171 | } 172 | ``` 173 | 174 | 如你所见,我们需要在`impl`行为`Foo`声明一个生命周期。我们重复了`'a`两次,就像在函数中:`impl<'a>`定义了一个生命周期`'a`,而`Foo<'a>`使用它。 175 | 176 | ## 多个生命周期 177 | 178 | 如果你有多个引用,你可以多次使用同一个生命周期: 179 | 180 | ```rust 181 | fn x_or_y<'a>(x: &'a str, y: &'a str) -> &'a str { 182 | # x 183 | # } 184 | ``` 185 | 186 | 这意味着`x`和`y`存活在同样的作用域内,并且返回值也同样存活在这个作用域内。如果你想要`x`和`y`有不同的生命周期,你可以使用多个生命周期参数: 187 | 188 | ```rust 189 | fn x_or_y<'a, 'b>(x: &'a str, y: &'b str) -> &'a str { 190 | # x 191 | # } 192 | ``` 193 | 194 | 在这个例子中,`x`和`y`有不同的有效的作用域,不过返回值和`x`有相同的生命周期 195 | 196 | ## 理解作用域(Thinking in scopes) 197 | 198 | 理解生命周期的一个办法是想象一个引用有效的作用域。例如: 199 | 200 | ```rust 201 | fn main() { 202 | let y = &5; // -+ `y` comes into scope. 203 | // | 204 | // Stuff... // | 205 | // | 206 | } // -+ `y` goes out of scope. 207 | ``` 208 | 209 | 加入我们的`Foo`: 210 | 211 | ```rust 212 | struct Foo<'a> { 213 | x: &'a i32, 214 | } 215 | 216 | fn main() { 217 | let y = &5; // -+ `y` comes into scope. 218 | let f = Foo { x: y }; // -+ `f` comes into scope. 219 | // Stuff... // | 220 | // | 221 | } // -+ `f` and `y` go out of scope. 222 | ``` 223 | 224 | 我们的`f`生存在`y`的作用域之中,所以一切正常。那么如果不是呢?下面的代码不能工作: 225 | 226 | ```rust 227 | struct Foo<'a> { 228 | x: &'a i32, 229 | } 230 | 231 | fn main() { 232 | let x; // -+ `x` comes into scope. 233 | // | 234 | { // | 235 | let y = &5; // ---+ `y` comes into scope. 236 | let f = Foo { x: y }; // ---+ `f` comes into scope. 237 | x = &f.x; // | | This causes an error. 238 | } // ---+ `f` and y go out of scope. 239 | // | 240 | println!("{}", x); // | 241 | } // -+ `x` goes out of scope. 242 | ``` 243 | 244 | 噢!就像你在这里看到的一样,`f`和`y`的作用域小于`x`的作用域。不过当我们尝试`x = &f.x`时,我们让`x`引用一些将要离开作用域的变量。 245 | 246 | 命名作用域用来赋予作用域一个名字。有了名字是我们可以谈论它的第一步。 247 | 248 | ## 'static 249 | 250 | 叫做`static`的生命周期是特殊的。它代表某样东西具有横跨整个程序的生命周期。大部分 Rust 程序员当他们处理字符串时第一次遇到`'static`: 251 | 252 | ```rust 253 | let x: &'static str = "Hello, world."; 254 | ``` 255 | 256 | 基本字符串是`&'static str`类型的因为它的引用一直有效:它们被写入了最终库文件的数据段。另一个例子是全局量: 257 | 258 | ```rust 259 | static FOO: i32 = 5; 260 | let x: &'static i32 = &FOO; 261 | ``` 262 | 263 | 它在二进制文件的数据段中保存了一个`i32`,而`x`是它的一个引用。 264 | 265 | ## 生命周期省略(Lifetime Elision) 266 | 267 | Rust支持强大的在函数体中的局部类型推断,不过这在项签名中是禁止的以便允许只通过项签名本身推导出类型。然而,出于人体工程学方面的考虑,有第二个非常限制的叫做“生命周期省略”的推断算法适用于函数签名。它只基于签名部分自身推断而不涉及函数体,只推断生命周期参数,并且只基于 3 个易于记忆和无歧义的规则,虽然并不隐藏它涉及到的实际类型,因为局部推断可能会适用于它。 268 | 269 | 当我们讨论生命周期省略的时候,我们使用**输入生命周期和输出生命周期**(*input lifetime and output lifetime.*)。**输入生命周期**是关于函数参数的,而**输出生命周期**是关于函数返回值的。例如,这个函数有一个输入生命周期: 270 | 271 | ```rust 272 | fn foo<'a>(bar: &'a str) 273 | ``` 274 | 275 | 这个有一个输出生命周期: 276 | 277 | ```rust 278 | fn foo<'a>() -> &'a str 279 | ``` 280 | 281 | 这个两者皆有: 282 | 283 | ```rust 284 | fn foo<'a>(bar: &'a str) -> &'a str 285 | ``` 286 | 287 | 有 3 条规则: 288 | 289 | * 每一个被省略的函数参数成为一个不同的生命周期参数。 290 | * 如果刚好有一个输入生命周期,不管是否省略,这个生命周期被赋予所有函数返回值中被省略的生命周期。 291 | * 如果有多个输入生命周期,不过它们当中有一个是`&self`或者`&mut self`,`self`的生命周期被赋予所有省略的输出生命周期。 292 | 293 | 否则,省略一个输出生命周期将是一个错误。 294 | 295 | ## 例子 296 | 297 | 这里有一些省略了生命周期的函数的例子。我们用它们的扩展形式配对了每个省略了生命周期的例子。 298 | 299 | ```rust 300 | fn print(s: &str); // elided 301 | fn print<'a>(s: &'a str); // expanded 302 | 303 | fn debug(lvl: u32, s: &str); // elided 304 | fn debug<'a>(lvl: u32, s: &'a str); // expanded 305 | ``` 306 | 307 | 在上面的例子中,`lvl`并不需要一个生命周期,因为它不是一个引用(`&`)。只有与引用(例如一个包含引用的`struct`)相关的变量才需要生命周期。 308 | 309 | ```rust 310 | fn substr(s: &str, until: u32) -> &str; // elided 311 | fn substr<'a>(s: &'a str, until: u32) -> &'a str; // expanded 312 | 313 | fn get_str() -> &str; // ILLEGAL, no inputs 314 | 315 | fn frob(s: &str, t: &str) -> &str; // ILLEGAL, two inputs 316 | fn frob<'a, 'b>(s: &'a str, t: &'b str) -> &str; // Expanded: Output lifetime is ambiguous 317 | 318 | fn get_mut(&mut self) -> &mut T; // elided 319 | fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded 320 | 321 | fn args(&mut self, args: &[T]) -> &mut Command; // elided 322 | fn args<'a, 'b, T: ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command; // expanded 323 | 324 | fn new(buf: &mut [u8]) -> BufWriter; // elided 325 | fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a>; // expanded 326 | ``` 327 | --------------------------------------------------------------------------------