rust book 中文翻译

mb2x 9年前

Rust编程语言

欢迎阅读!这本书将教会你使用Rust编程语言。 Rust是一个注重安全与速度的现代系统编程语言,通过在没有垃圾回收的情况下保证内存安全来实现它的目标,这使它成为一个在很多其它语言不适合的用例中 大展身手的语言:嵌入到其它语言中,在特定的时间和空间要求下编程,和编写底层代码,例如设备驱动和操作系统。它通过一系列的不产生运行时开销的编译时安 全检查来提升目前语言所关注的这个领域,同时消除一切数据竞争。Rust同时也意在实现“零开销抽象”,即便在这些抽象看起来比较像一个高级语言的特性。 即便如此,Rust也允许你像一个底层语言那样进行精确的控制。

《Rust编程语言》被分为7个部分。这个介绍是第一部分。之后是:

在阅读了介绍这部分之后,你可以根据喜好深入到“学习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中,你并不需要自己调用像mallocfree这样的函数:编译器静态决定何时你需要分配和销毁内存,并自动调用这些函数。人非圣贤孰能无过,不过编译器永远也不会忘记。

让我们为例子再加一行:

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()之前离开作用域,所以我们不会碰到问题。

所有权的概念并不仅仅善于防止悬垂指针,也解决了一整个系列的相关问题,比如迭代器无效,并发和其它问题。