rust book 中文翻译
Rust编程语言
欢迎阅读!这本书将教会你使用Rust编程语言。 Rust是一个注重安全与速度的现代系统编程语言,通过在没有垃圾回收的情况下保证内存安全来实现它的目标,这使它成为一个在很多其它语言不适合的用例中 大展身手的语言:嵌入到其它语言中,在特定的时间和空间要求下编程,和编写底层代码,例如设备驱动和操作系统。它通过一系列的不产生运行时开销的编译时安 全检查来提升目前语言所关注的这个领域,同时消除一切数据竞争。Rust同时也意在实现“零开销抽象”,即便在这些抽象看起来比较像一个高级语言的特性。 即便如此,Rust也允许你像一个底层语言那样进行精确的控制。
《Rust编程语言》被分为7个部分。这个介绍是第一部分。之后是:
- 准备 - 为你的电脑安装Rust开发环境
- 学习Rust - 通过小项目来学习Rust编程
- 高效Rust - 编写优秀Rust代码的高级内容
- 语法和语义 - Rust各个部分,被拆分成小的部分讲解
- Rust开发版 - 还未出现在稳定版本中的最新功能
- 词汇表 - 书中使用的术语的参考
- 学院派研究 - 影响过Rust的文献
在阅读了介绍这部分之后,你可以根据喜好深入到“学习Rust”或“语法和语义”部分:如果你想通过项目深入了解,可以先选择“学习Rust”;如果你想从头开始,并且学习一个完整的内容再学习另一个,你可以从“语法和语义”开始。丰富的交叉连接将这些部分联系到一起。
贡献
生成这本书的源文件可以在GitHub上找到:github.com/rust-lang/rust/tree/master/src/doc/trpl
Rust简介
Rust是你会感兴趣的语言吗?让我们检查一些小的代码例子来展示它的部分威力。
使Rust显得独一无二的主要概念是“所有权”。考虑这个小例子:
fn main() { let mut x = vec!["Hello", "world"]; }
这个程序创建了一个叫做x
的变量绑定。这个绑定的值是一个Vec<T>
,一个vector,我们通过一个定义在标准库中的宏来创建它。这个宏叫做vec
,并且我们通过一个!
调用宏。这遵循了Rust的一般原则:让一切明了。宏可以做比函数调用复杂的多的多的工作,并且它们在视觉上也是有区别的。!
也方便了解析,更容易编写工具,这也是很重要的。
我们使用了mut
来使x
可变:再Rust中绑定是默认是不可变的。在下面的例子中这个vector是可变的。
另外值得注意的是这里我们并不需要一个类型注释:因为Rust是静态类型的,我们并不需要显式的标明类型。Rust拥有类型推断来平衡静态类型的能力和类型注释的冗余。
Rust与堆分配相比倾向于栈分配:x
被直接储存在栈上。然而,Vec<T>
类型在堆上为vector的元素分配了空间。如果你并不熟悉这里的区别,目前你可以忽略它,或者看看“栈与堆”。作为一个系统编程语言,Rust给予你控制内存分配的能力,不过当我们上手后,这并不是什么大问题。
之前,我们提到“所有权”是Rust中的一个关键概念。在Rust用语中,x
被认为“拥有”这个vector。这意味着当x
离开作用域,vector的内存将被销毁。这由Rust编译器决定,而不是通过类似垃圾回收器这样的机制。换句话说,在Rust中,你并不需要自己调用像malloc
和free
这样的函数:编译器静态决定何时你需要分配和销毁内存,并自动调用这些函数。人非圣贤孰能无过,不过编译器永远也不会忘记。
让我们为例子再加一行:
fn main() { let mut x = vec!["Hello", "world"]; let y = &x[0]; }
我们引入了另一个绑定,y
。在这个例子中,y
是对vector第一个元素的“引用”。Rust的引用类似于其它语言中的指针,不过带有额外的编译时安全检查。引用用“借用”它指向的内容,而不是拥有它,来与所有权系统交互。这里的区别是,当一个引用离开作用域,它不会释放之下的内存。如果它这么做了,我们会释放两次,这是很糟的!。
让我们增加第三行。这看起来并不会引起错误,不过实际上会造成一个编译错误:
fn main() { let mut x = vec!["Hello", "world"]; let y = &x[0]; x.push("foo"); }
push
是vector的一个方法,它在vector的末尾附加另一个元素。当我们尝试编译这个程序时,我们得到一个错误:
error: cannot borrow `x` as mutable because it is also borrowed as immutable x.push(4); ^ note: previous borrow of `x` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `x` until the borrow ends let y = &x[0]; ^ note: previous borrow ends here fn main() { } ^
噢!Rust编译器有时给出灰常详细的错误,而这就是其中之一。正如错误所解释的,当我们让绑定可变,我们仍不能调用push
。这是因为我们已经有了一个vector元素的引用,y
。 当有其它引用存在时改变值是危险的,因为我们可能使这个引用无效。在这个特定的例子中,当我们创建了vector,我们可能只分配了3个元素的空间。增加 一个元素意味着将分配一个新的能放下所有4个元素的空间,拷贝旧的值,并更新内部的指针指向这个内存。所有这些都木有问题。问题是y
并没有被更新,很糟糕地,y
成了一个“悬垂指针”(dangling pointer)。因此,在这个例子中任何对y
的使用都会引起错误,而编译器会为我们捕获了这个错误。
那么我们应该如何解决这个问题呢?这里我们可以采取两个方法。第一个方法是使用拷贝而不使用引用:
fn main() { let mut x = vec!["Hello", "world"]; let y = x[0].clone(); x.push("foo"); }
Rust默认拥有移动语义,所以如果我们想要拷贝一些数据,我们调用clone()
方法。在这个例子中,y
不再是一个储存在x
中vector的一个引用,而是它第一个元素的拷贝,"hello"
。现在我们并不拥有一个引用,所以push()
就能正常工作。
如果我们真心需要一个引用,我们需要另一种方法:确保在我们尝试修改之前,让引用离开作用域。如下:
fn main() { let mut x = vec!["Hello", "world"]; { let y = &x[0]; } x.push("foo"); }
我们用一对大括号创建了一个内部作用域,y
会在我们调用push()
之前离开作用域,所以我们不会碰到问题。
所有权的概念并不仅仅善于防止悬垂指针,也解决了一整个系列的相关问题,比如迭代器无效,并发和其它问题。