Rust半小时教程

原文: A half-hour to learn Rust

为了熟练的掌握一门编程语言,人们不得不阅读它的大量的相关资料。但是如果你不理解这些资料介绍的内容,如何能学习更多的资料呢?
在本文中,我将尽可能多地列举Rust代码片段,并解释它们包含的关键字和符号的含义,而不是只关注Rust的一两个概念。

准备好了吗,让我们出发吧!

let 引入变量

1
2
let x; // declare "x"
x = 42; // assign 42 to "x"

也可以写成一行:

1
let x = 42;

你可以使用:显式地指定变量的类型,也就是类型注解:

1
2
3
4
5
let x: i32; // `i32` is a signed 32-bit integer
x = 42;
// there's i8, i16, i32, i64, i128
// also u8, u16, u32, u64, u128 for unsigned

也可以写成一行:

1
let x: i32 = 42;

如果你声明一个变量,后来再初始化它。在初始化之前使用它的话编译器会报错:

1
2
3
let x;
foobar(x); // error: borrow of possibly-uninitialized variable: `x`
x = 42;

下面的代码就可以了:

1
2
3
let x;
x = 42;
foobar(x); // the type of `x` will be inferred from here

下划线_是一个特殊的名字,或者更确切地说,“缺乏名字”。基本上它的意思就是扔掉一些东西:

1
2
3
4
5
// this does *nothing* because 42 is a constant
let _ = 42;
// this calls `get_thing` but throws away its result
let _ = get_thing();

以下划线开头的名称是常规名称,有一点特殊,就是如果它们未被使用的话编译器不会报警告:

1
2
3
// we may use `_x` eventually, but our code is a work-in-progress
// and we just wanted to get rid of a compiler warning for now.
let _x = 42;

变量名可以重用-它会隐藏(shadow)前一个变量:

1
2
3
4
let x = 13;
let x = x + 3;
// using `x` after that line only refers to the second `x`,
// the first `x` no longer exists.

Rust有tuple类型,你可以把它看作有固定长度的不同类型的集合:

1
2
3
let pair = ('a', 17);
pair.0; // this is 'a'
pair.1; // this is 17

如果我们想为tuple加上类型注解,可以这么做:

1
let pair: (char, i32) = ('a', 17);

Tuple类型可以通过赋值方式进行解构(destructured),这意味着它们被分成各自独立的字段:

1
2
let (some_char, some_int) = ('a', 17);
// now, `some_char` is 'a', and `some_int` is 17

当一个函数返回tuple类型的时候特别管用:

1
let (left, right) = slice.split_at(middle);

当然,解构一个tuple的时候,下划线_可以用来丢掉一些字段:

1
let (_, right) = slice.split_at(middle);

分号;放在语句(statement)的结尾:

1
2
3
let x = 3;
let y = 5;
let z = y + x;

这意味着语句可以写成多行:

1
2
3
4
let x = vec![1, 2, 3, 4, 5, 6, 7, 8]
.iter()
.map(|x| x + 3)
.fold(0, |x, y| x + y);

(之后我们再介绍这段代码的意义)

fn用来声明一个函数。
下面是一个void函数:

1
2
3
fn greet() {
println!("Hi there!");
}

下面是一个返回32位有符号的整数,箭头指示它的返回类型:

1
2
3
fn fair_dice_roll() -> i32 {
4
}

一对大括号声明了代码块(block),块有自己的作用域:

1
2
3
4
5
6
7
8
9
10
// This prints "in", then "out"
fn main() {
let x = "out";
{
// this is a different `x`
let x = "in";
println!(x);
}
println!(x);
}

块也是表达式,意味着它的计算结果是一个值:

1
2
3
4
5
// this:
let x = 42;
// is equivalent to this:
let x = { 42 };

块可以包括多条语句:

1
2
3
4
5
let x = {
let y = 1; // first statement
let z = 2; // second statement
y + z // this is the *tail* - what the whole block will evaluate to
};

函数块的最后省略分号意味着返回这个值,例如下面两个函数功能是一样的:

1
2
3
4
5
6
7
fn fair_dice_roll() -> i32 {
return 4;
}
fn fair_dice_roll() -> i32 {
4
}

if条件也可以是表达式:

1
2
3
4
5
6
7
fn fair_dice_roll() -> i32 {
if feeling_lucky {
6
} else {
4
}
}

match也是表达式:

1
2
3
4
5
6
fn fair_dice_roll() -> i32 {
match feeling_lucky {
true => 6,
false => 4,
}
}

点号(.)用来访问一个值的字段:

1
2
3
4
5
let a = (10, 20);
a.0; // this is 10
let amos = get_some_struct();
amos.nickname; // this is "fasterthanlime"

或者调用一个方法:

1
2
let nick = "fasterthanlime";
nick.len(); // this is 14

双冒号(::)类似点号但是操作的对象是命名空间。
下面的例子中std是一个crate(库),cmp是一个模块(module, 源文件),min是一个函数:

1
let least = std::cmp::min(3, 8); // this is 3

use指令将其它命名空间的名称引入到当前:

1
2
3
use std::cmp::min;
let least = min(7, 1); // this is 1

使用use指令的时候,大括号意味着一组名称(glob)。如果我们想同时引入maxmin,我们可以这么做:

1
2
3
4
5
6
7
8
9
// this works:
use std::cmp::min;
use std::cmp::max;
// this also works:
use std::cmp::{min, max};
// this also works!
use std::{cmp::min, cmp::max};

通配符*引入命名空间下的所有的名称:

1
2
// this brings `min` and `max` in scope, and many other things
use std::cmp::*;

类型也是命名空间,它们的方法也可以通过普通函数一样调用:

1
2
let x = "amos".len(); // this is 4
let x = str::len("amos"); // this is also 4

str是一个基本类型(primitive type),但是在默认的命名空间下也有很多非基本类型:

1
2
3
4
5
// `Vec` is a regular struct, not a primitive type
let v = Vec::new();
// this is exactly the same code, but with the *full* path to `Vec`
let v = std::vec::Vec::new();

这是因为Rust会在每个模块开始之前插入:

1
use std::prelude::v1::*;

它重新输出了很多的符号,比如VecStringOptionResult

结构体使用struct声明:

1
2
3
4
struct Vec2 {
x: f64, // 64-bit floating point, aka "double precision"
y: f64,
}

可以使用结构体文本初始化它们:

1
2
3
let v1 = Vec2 { x: 1.0, y: 3.0 };
let v2 = Vec2 { y: 2.0, x: 4.0 };
// the order does not matter, only the names do

有一个简洁的方式使用另外一个结构体初始化余下的字段:

1
2
3
4
let v3 = Vec2 {
x: 14.0,
..v2
};

这被称作struct update syntax,只能发生在最后的位置,后面没有逗号。

注意"剩余的字段"可以是结构体的所有字段:

1
let v4 = Vec2 { ..v3 };

结构体,也像tuple类型一样,可以解构。

下面是一个合法的let模式:

1
let (left, right) = slice.split_at(middle);

又比如下面的代码:

1
2
3
let v = Vec2 { x: 3.0, y: 6.0 };
let Vec2 { x, y } = v;
// `x` is now 3.0, `y` is now `6.0`

抑或下面的代码:

1
2
let Vec2 { x, .. } = v;
// this throws away `v.y`

let模式也可以用在if中当条件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
struct Number {
odd: bool,
value: i32,
}
fn main() {
let one = Number { odd: true, value: 1 };
let two = Number { odd: false, value: 2 };
print_number(one);
print_number(two);
}
fn print_number(n: Number) {
if let Number { odd: true, value } = n {
println!("Odd number: {}", value);
} else if let Number { odd: false, value } = n {
println!("Even number: {}", value);
}
}
// this prints:
// Odd number: 1
// Even number: 2

match匹配也是模式,就像if let:

1
2
3
4
5
6
7
8
fn print_number(n: Number) {
match n {
Number { odd: true, value } => println!("Odd number: {}", value),
Number { odd: false, value } => println!("Even number: {}", value),
}
}
// this prints the same as before

注意match匹配必须是详尽的,至少需要一个分支与之匹配:

1
2
3
4
5
6
7
8
fn print_number(n: Number) {
match n {
Number { value: 1, .. } => println!("One"),
Number { value: 2, .. } => println!("Two"),
Number { value, .. } => println!("{}", value),
// if that last arm didn't exist, we would get a compile-time error
}
}

如果你觉得麻烦,可以使用下划线_匹配所有的模式:

1
2
3
4
5
6
7
fn print_number(n: Number) {
match n.value {
1 => println!("One"),
2 => println!("Two"),
_ => println!("{}", n.value),
}
}

你可以为你的类型声明方法:

1
2
3
4
5
6
7
8
9
10
struct Number {
odd: bool,
value: i32,
}
impl Number {
fn is_strictly_positive(self) -> bool {
self.value > 0
}
}

可以正常使用它们:

1
2
3
4
5
6
7
8
fn main() {
let minus_two = Number {
odd: false,
value: -2,
};
println!("positive? {}", minus_two.is_strictly_positive());
// this prints "positive? false"
}

默认变量绑定是不可变的:

1
2
3
4
5
6
7
8
fn main() {
let n = Number {
odd: true,
value: 17,
};
n.odd = false; // error: cannot assign to `n.odd`,
// as `n` is not declared to be mutable
}

不可变的变量不能对其变量值进行修改,但是同时也不能通过赋值更改变量:

1
2
3
4
5
6
7
8
9
10
fn main() {
let n = Number {
odd: true,
value: 17,
};
n = Number {
odd: false,
value: 22,
}; // error: cannot assign twice to immutable variable `n`
}

mut允许变量可以更改:

1
2
3
4
5
6
7
fn main() {
let mut n = Number {
odd: true,
value: 17,
}
n.value = 19; // all good
}

trait是多个类型拥有的共同的东西:

1
2
3
trait Signed {
fn is_strictly_negative(self) -> bool;
}

你可以实现:

  • 为任意类型实现你自己定义的trait
  • 为你的类型实现任意类型的trait
  • 不允许为别人的类型实现别人的trait

这被称之为孤立规则(orphan rules)。

下面的例子是自定义类型实现自定义trait:

1
2
3
4
5
6
7
8
9
10
impl Signed for Number {
fn is_strictly_negative(self) -> bool {
self.value < 0
}
}
fn main() {
let n = Number { odd: false, value: -44 };
println!("{}", n.is_strictly_negative()); // prints "true"
}

为外部类型实现自定义trait(甚至是基本类型):

1
2
3
4
5
6
7
8
9
10
impl Signed for i32 {
fn is_strictly_negative(self) -> bool {
self < 0
}
}
fn main() {
let n: i32 = -44;
println!("{}", n.is_strictly_negative()); // prints "true"
}

为自定义类型实现外部trait:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// the `Neg` trait is used to overload `-`, the
// unary minus operator.
impl std::ops::Neg for Number {
type Output = Number;
fn neg(self) -> Number {
Number {
value: -self.value,
odd: self.odd,
}
}
}
fn main() {
let n = Number { odd: true, value: 987 };
let m = -n; // this is only possible because we implemented `Neg`
println!("{}", m.value); // prints "-987"
}

impl总是用来为类型实现方法的,所以在这个块中,Self意味着这个类型:

1
2
3
4
5
6
7
8
9
10
impl std::ops::Neg for Number {
type Output = Self;
fn neg(self) -> Self {
Self {
value: -self.value,
odd: self.odd,
}
}
}

有些trait只是一个标记(marker),它并不是指示类型要实现什么方法,而是说这种类型可以用作做特定的事情。
例如i32实现Copy trait(简单地讲,i32是可复制的),所以下面的代码工作正常:

1
2
3
4
5
fn main() {
let a: i32 = 15;
let b = a; // `a` is copied
let c = a; // `a` is copied again
}

下面的代码也正常:

1
2
3
4
5
6
7
8
9
fn print_i32(x: i32) {
println!("x = {}", x);
}
fn main() {
let a: i32 = 15;
print_i32(a); // `a` is copied
print_i32(a); // `a` is copied again
}

但是Number类型没有实现Copy,所以下面的代码不工作:

1
2
3
4
5
fn main() {
let n = Number { odd: true, value: 51 };
let m = n; // `n` is moved into `m`
let o = n; // error: use of moved value: `n`
}

下面的代码也不行:

1
2
3
4
5
6
7
8
9
fn print_number(n: Number) {
println!("{} number {}", if n.odd { "odd" } else { "even" }, n.value);
}
fn main() {
let n = Number { odd: true, value: 51 };
print_number(n); // `n` is moved
print_number(n); // error: use of moved value: `n`
}

但是如果print_number使用一个不可变的引用就可以:

1
2
3
4
5
6
7
8
9
fn print_number(n: &Number) {
println!("{} number {}", if n.odd { "odd" } else { "even" }, n.value);
}
fn main() {
let n = Number { odd: true, value: 51 };
print_number(&n); // `n` is borrowed for the time of the call
print_number(&n); // `n` is borrowed again
}

如果变量被声明为可变的,则函数参数使用可变引用也可以工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fn invert(n: &mut Number) {
n.value = -n.value;
}
fn print_number(n: &Number) {
println!("{} number {}", if n.odd { "odd" } else { "even" }, n.value);
}
fn main() {
// this time, `n` is mutable
let mut n = Number { odd: true, value: 51 };
print_number(&n);
invert(&mut n); // `n is borrowed mutably - everything is explicit
print_number(&n);
}

Trait方法中的self参数可以使用引用,也可以使用不可变引用。

1
2
3
4
impl std::clone::Clone for Number {
fn clone(&self) -> Self {
Self { ..*self }
}

当调用trait的方法时,receiver隐式地被借用:

1
2
3
4
5
6
7
8
fn main() {
let n = Number { odd: true, value: 51 };
let mut m = n.clone();
m.value += 100;
print_number(&n);
print_number(&m);
}

前面讲到过,它和下面的代码一样:

1
2
3
let m = n.clone();
let m = std::clone::Clone::clone(&n);

Copy这样的Marker trait是没有方法的:

1
2
3
4
5
6
7
8
// note: `Copy` requires that `Clone` is implemented too
impl std::clone::Clone for Number {
fn clone(&self) -> Self {
Self { ..*self }
}
}
impl std::marker::Copy for Number {}

现在Clone仍然可以使用:

1
2
3
4
5
fn main() {
let n = Number { odd: true, value: 51 };
let m = n.clone();
let o = n.clone();
}

但是Number值不会被move了:

1
2
3
4
5
fn main() {
let n = Number { odd: true, value: 51 };
let m = n; // `m` is a copy of `n`
let o = n; // same. `n` is neither moved nor borrowed.
}

一些trait太通用了,我们可以通过derive属性自动实现它们:

1
2
3
4
5
6
7
#[derive(Clone, Copy)]
struct Number {
odd: bool,
value: i32,
}
// this expands to `impl Clone for Number` and `impl Copy for Number` blocks.

函数可以是泛型的:

1
2
3
fn foobar<T>(arg: T) {
// do something with `arg`
}

它们可以有多个类型参数,类型参数用在函数声明和函数体中,用来替代具体的类型:

1
2
3
fn foobar<L, R>(left: L, right: R) {
// do something with `left` and `right`
}

类型参数通常有约束,所以你可以用它做一些额外的事情。

最简单的约束就是trait名称:

1
2
3
4
5
6
7
fn print<T: Display>(value: T) {
println!("value = {}", value);
}
fn print<T: Debug>(value: T) {
println!("value = {:?}", value);
}

类型约束还可以使用更长的语法:

1
2
3
4
5
6
fn print<T>(value: T)
where
T: Display,
{
println!("value = {}", value);
}

约束还可以更复杂,比如要求类型要实现多个trait:

1
2
3
4
5
6
7
8
9
10
11
12
13
use std::fmt::Debug;
fn compare<T>(left: T, right: T)
where
T: Debug + PartialEq,
{
println!("{:?} {} {:?}", left, if left == right { "==" } else { "!=" }, right);
}
fn main() {
compare("tea", "coffee");
// prints: "tea" != "coffee"
}

泛型函数可以被当作一个命名空间,包含无穷多个不同具体类型的函数。

类似cratemodule和类型,泛型函数可以使用::导航:

1
2
3
4
5
fn main() {
use std::any::type_name;
println!("{}", type_name::<i32>()); // prints "i32"
println!("{}", type_name::<(f64, char)>()); // prints "(f64, char)"
}

这被亲切地称之为turbofish syntax,因为::<>看起来像条鱼。

结构体可以是泛型的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
struct Pair<T> {
a: T,
b: T,
}
fn print_type_name<T>(_val: &T) {
println!("{}", std::any::type_name::<T>());
}
fn main() {
let p1 = Pair { a: 3, b: 9 };
let p2 = Pair { a: true, b: false };
print_type_name(&p1); // prints "Pair<i32>"
print_type_name(&p2); // prints "Pair<bool>"
}

标准库中的Vec(分配在堆上的数组)就是泛型实现的:

1
2
3
4
5
6
7
8
fn main() {
let mut v1 = Vec::new();
v1.push(1);
let mut v2 = Vec::new();
v2.push(false);
print_type_name(&v1); // prints "Vec<i32>"
print_type_name(&v2); // prints "Vec<bool>"
}

谈到Vec,有个宏(macro)可以通过字面方式声明Vec变量:

1
2
3
4
5
6
fn main() {
let v1 = vec![1, 2, 3];
let v2 = vec![true, false, true];
print_type_name(&v1); // prints "Vec<i32>"
print_type_name(&v2); // prints "Vec<bool>"
}

类似name!()name![]name!{}都是调用宏的方式。宏会被展开成正常的代码。

事实上,println就是一个宏:

1
2
3
fn main() {
println!("{}", "Hello there!");
}

它的展开代码和下面的代码功能一样:

1
2
3
4
fn main() {
use std::io::{self, Write};
io::stdout().lock().write_all(b"Hello there!\n").unwrap();
}

panic也是一个宏,例如Option既可以包含某个值,也可以不包含值。如果它不包含值,调用它的.unwrap()会panic:

1
2
3
4
5
6
7
8
9
fn main() {
let o1: Option<i32> = Some(128);
o1.unwrap(); // this is fine
let o2: Option<i32> = None;
o2.unwrap(); // this panics!
}
// output: thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/libcore/option.rs:378:21

Option并不是一个结构体,而是一个枚举类型(enum),它包含两个值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
enum Option<T> {
None,
Some(T),
}
impl<T> Option<T> {
fn unwrap(self) -> T {
// enums variants can be used in patterns:
match self {
Self::Some(t) => t,
Self::None => panic!(".unwrap() called on a None option"),
}
}
}
use self::Option::{None, Some};
fn main() {
let o1: Option<i32> = Some(128);
o1.unwrap(); // this is fine
let o2: Option<i32> = None;
o2.unwrap(); // this panics!
}
// output: thread 'main' panicked at '.unwrap() called on a None option', src/main.rs:11:27

Result也是一个枚举类型。它既可以包含一个正常的,也可以包含一个error:

1
2
3
4
enum Result<T, E> {
Ok(T),
Err(E),
}

如果包含error,调用它的.unwrap()也会panic。

变量绑定有声明周期:

1
2
3
4
5
6
7
8
9
fn main() {
// `x` doesn't exist yet
{
let x = 42; // `x` starts existing
println!("x = {}", x);
// `x` stops existing
}
// `x` no longer exists
}

类似地,引用也有声明周期:

1
2
3
4
5
6
7
8
9
10
11
fn main() {
// `x` doesn't exist yet
{
let x = 42; // `x` starts existing
let x_ref = &x; // `x_ref` starts existing - it borrows `x`
println!("x_ref = {}", x_ref);
// `x_ref` stops existing
// `x` stops existing
}
// `x` no longer exists
}

引用的声明周期不能超过它借用的变量的声明周期:

1
2
3
4
5
6
7
8
fn main() {
let x_ref = {
let x = 42;
&x
};
println!("x_ref = {}", x_ref);
// error: `x` does not live long enough
}

一个变量可以不可变地借用多次:

1
2
3
4
5
6
7
fn main() {
let x = 42;
let x_ref1 = &x;
let x_ref2 = &x;
let x_ref3 = &x;
println!("{} {} {}", x_ref1, x_ref2, x_ref3);
}

在借用的时候,变量不能被修改:

1
2
3
4
5
6
7
fn main() {
let mut x = 42;
let x_ref = &x;
x = 13;
println!("x_ref = {}", x_ref);
// error: cannot assign to `x` because it is borrowed
}

当不可变地借用时,不能同时可变地的借用:

1
2
3
4
5
6
7
fn main() {
let mut x = 42;
let x_ref1 = &x;
let x_ref2 = &mut x;
// error: cannot borrow `x` as mutable because it is also borrowed as immutable
println!("x_ref1 = {}", x_ref1);
}

函数参数中的引用也有生命周期:

1
2
3
4
fn print(x: &i32) {
// `x` is borrowed (from the outside) for the
// entire time this function is called.
}

函数中的参数被调用时可以同时使用多个的生命周期:

  • 所有使用这些引用的函数都是泛型的
  • 声明周期也是泛型参数

生命周期的名称以'开始:

1
2
3
4
5
// elided (non-named) lifetimes:
fn print(x: &i32) {}
// named lifetimes:
fn print<'a>(x: &'a i32) {}

返回引用的声明周期依赖参数的声明周期:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
struct Number {
value: i32,
}
fn number_value<'a>(num: &'a Number) -> &'a i32 {
&num.value
}
fn main() {
let n = Number { value: 47 };
let v = number_value(&n);
// `v` borrows `n` (immutably), thus: `v` cannot outlive `n`.
// While `v` exists, `n` cannot be mutably borrowed, mutated, moved, etc.
}

当只有一个生命周期时,它并需要被命名,所有对象都有同样的声明周期,所以下面两个函数是等价的:

1
2
3
4
5
6
7
fn number_value<'a>(num: &'a Number) -> &'a i32 {
&num.value
}
fn number_value(num: &Number) -> &i32 {
&num.value
}

结构体也可以通过生命周期声明为泛型,这允许它们持有引用:

1
2
3
4
5
6
7
8
9
struct NumRef<'a> {
x: &'a i32,
}
fn main() {
let x: i32 = 99;
let x_ref = NumRef { x: &x };
// `x_ref` cannot outlive `x`, etc.
}

同样的代码,增加一个函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct NumRef<'a> {
x: &'a i32,
}
fn as_num_ref<'a>(x: &'a i32) -> NumRef<'a> {
NumRef { x: &x }
}
fn main() {
let x: i32 = 99;
let x_ref = as_num_ref(&x);
// `x_ref` cannot outlive `x`, etc.
}

同样的代码,使用省略的(elided)的生命周期:

1
2
3
4
5
6
7
8
9
10
11
12
13
struct NumRef<'a> {
x: &'a i32,
}
fn as_num_ref(x: &i32) -> NumRef<'_> {
NumRef { x: &x }
}
fn main() {
let x: i32 = 99;
let x_ref = as_num_ref(&x);
// `x_ref` cannot outlive `x`, etc.
}

impl块也可以使用声明周期实现泛型:

1
2
3
4
5
6
7
8
9
10
11
12
impl<'a> NumRef<'a> {
fn as_i32_ref(&'a self) -> &'a i32 {
self.x
}
}
fn main() {
let x: i32 = 99;
let x_num_ref = NumRef { x: &x };
let x_i32_ref = x_num_ref.as_i32_ref();
// neither ref cannot outlive `x`
}

但是你同样可以使用省略的方式:

1
2
3
4
5
impl<'a> NumRef<'a> {
fn as_i32_ref(&self) -> &i32 {
self.x
}
}

如果你不需要使用声明周期的名字,你甚至可以省略更多:

1
2
3
4
5
impl NumRef<'_> {
fn as_i32_ref(&self) -> &i32 {
self.x
}
}

有一个特殊的声明周期,叫做'static,它的生命周期在整个程序运行时。

字符串字面值就是'static:

1
2
3
4
5
6
7
8
9
struct Person {
name: &'static str,
}
fn main() {
let p = Person {
name: "fasterthanlime",
};
}

但是owned string声明周期就不是'static的:

1
2
3
4
5
6
7
8
9
struct Person {
name: &'static str,
}
fn main() {
let name = format!("fasterthan{}", "lime");
let p = Person { name: &name };
// error: `name` does not live long enough
}

上面的例子中name并不是&'static str类型,而是Stirng类型。它是动态分配的,可以被释放。它的生命周期小于整个程序,尽管它是在main函数中。

为了在Person中存储一个非'static的字符串,你需要:

A) 通过声明周期声明泛型

1
2
3
4
5
6
7
8
9
struct Person<'a> {
name: &'a str,
}
fn main() {
let name = format!("fasterthan{}", "lime");
let p = Person { name: &name };
// `p` cannot outlive `name`
}

或者

B) 获得这个字符串的所有权

1
2
3
4
5
6
7
8
9
struct Person {
name: String,
}
fn main() {
let name = format!("fasterthan{}", "lime");
let p = Person { name: name };
// `name` was moved into `p`, their lifetimes are no longer tied.
}

在一个结构体的字面值中,如果字段名和变量相同时:

1
let p = Person { name: name };

可以简写为:

1
let p = Person { name };

Rust中很多类型都有owned和非owned变种:

  • 字符串: String是owned, &str是引用
  • 路径:PathBuf是owned, &Path是引用
  • 集合: Vec<T>是owned, &[T]是引用

Rust有slice - 它们是对多个连续元素的引用。
你可以借用vector的slice,例如:

1
2
3
4
5
6
7
8
fn main() {
let v = vec![1, 2, 3, 4, 5];
let v2 = &v[2..4];
println!("v2 = {:?}", v2);
}
// output:
// v2 = [3, 4]

上面并没有什么魔法。索引操作符(foo[index])被IndexIndexMut trait重载。
..是range表示方法。 Range是标准库定义的一组结构体。
前后的索引值可以省略,默认右边的值是不包含的, 如果要包含右边的值,使用=,:

1
2
3
4
5
6
7
8
9
10
fn main() {
// 0 or greater
println!("{:?}", (0..).contains(&100)); // true
// strictly less than 20
println!("{:?}", (..20).contains(&20)); // false
// 20 or less than 20
println!("{:?}", (..=20).contains(&20)); // true
// only 3, 4, 5
println!("{:?}", (3..6).contains(&4)); // true
}

借用规则同样应用于slice:

1
2
3
4
5
6
7
8
9
fn tail(s: &[u8]) -> &[u8] {
&s[1..]
}
fn main() {
let x = &[1, 2, 3, 4, 5];
let y = tail(x);
println!("y = {:?}", y);
}

与下面的代码相同:

1
2
3
fn tail<'a>(s: &'a [u8]) -> &'a [u8] {
&s[1..]
}

下面的代码是合法的:

1
2
3
4
5
6
7
fn main() {
let y = {
let x = &[1, 2, 3, 4, 5];
tail(x)
};
println!("y = {:?}", y);
}

只不过是因为[1, 2, 3, 4, 5]'static。下面的代码就不合法:

1
2
3
4
5
6
7
8
fn main() {
let y = {
let v = vec![1, 2, 3, 4, 5];
tail(&v)
// error: `v` does not live long enough
};
println!("y = {:?}", y);
}

这是因为vector分配在堆上,它有非'static的声明周期。

&str实际上是slice:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fn file_ext(name: &str) -> Option<&str> {
// this does not create a new string - it returns
// a slice of the argument.
name.split(".").last()
}
fn main() {
let name = "Read me. Or don't.txt";
if let Some(ext) = file_ext(name) {
println!("file extension: {}", ext);
} else {
println!("no file extension");
}
}

借用规则同样适用:

1
2
3
4
5
6
7
8
fn main() {
let ext = {
let name = String::from("Read me. Or don't.txt");
file_ext(&name).unwrap_or("")
// error: `name` does not live long enough
};
println!("extension: {:?}", ext);
}

返回失败的函数典型地返回Result:

1
2
3
4
5
6
7
8
9
fn main() {
let s = std::str::from_utf8(&[240, 159, 141, 137]);
println!("{:?}", s);
// prints: Ok("🍉")
let s = std::str::from_utf8(&[195, 40]);
println!("{:?}", s);
// prints: Err(Utf8Error { valid_up_to: 0, error_len: Some(1) })
}

如果处理失败的时候想panic,你可以调用.unwrap():

1
2
3
4
5
6
7
8
9
10
fn main() {
let s = std::str::from_utf8(&[240, 159, 141, 137]).unwrap();
println!("{:?}", s);
// prints: "🍉"
let s = std::str::from_utf8(&[195, 40]).unwrap();
// prints: thread 'main' panicked at 'called `Result::unwrap()`
// on an `Err` value: Utf8Error { valid_up_to: 0, error_len: Some(1) }',
// src/libcore/result.rs:1165:5
}

或者调用.expect() panic一个定制的信息:

1
2
3
4
5
fn main() {
let s = std::str::from_utf8(&[195, 40]).expect("valid utf-8");
// prints: thread 'main' panicked at 'valid utf-8: Utf8Error
// { valid_up_to: 0, error_len: Some(1) }', src/libcore/result.rs:1165:5
}

抑或使用match:

1
2
3
4
5
6
7
fn main() {
match std::str::from_utf8(&[240, 159, 141, 137]) {
Ok(s) => println!("{}", s),
Err(e) => panic!(e),
}
// prints 🍉
}

甚至使用if let:

1
2
3
4
5
6
fn main() {
if let Ok(s) = std::str::from_utf8(&[240, 159, 141, 137]) {
println!("{}", s);
}
// prints 🍉
}

再不济给上层抛出错误:

1
2
3
4
5
6
7
fn main() -> Result<(), std::str::Utf8Error> {
match std::str::from_utf8(&[240, 159, 141, 137]) {
Ok(s) => println!("{}", s),
Err(e) => return Err(e),
}
Ok(())
}

常用的简洁方式是使用?:

1
2
3
4
5
fn main() -> Result<(), std::str::Utf8Error> {
let s = std::str::from_utf8(&[240, 159, 141, 137])?;
println!("{}", s);
Ok(())
}

*符号常用来解引用,但是你不需要专门访问字段或者调用方法:

1
2
3
4
5
6
7
8
9
10
11
12
struct Point {
x: f64,
y: f64,
}
fn main() {
let p = Point { x: 1.0, y: 3.0 };
let p_ref = &p;
println!("({}, {})", p_ref.x, p_ref.y);
}
// prints `(1, 3)`

如果类型是可复制的(实现Copy),那么你可以:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
struct Point {
x: f64,
y: f64,
}
fn negate(p: Point) -> Point {
Point {
x: -p.x,
y: -p.y,
}
}
fn main() {
let p = Point { x: 1.0, y: 3.0 };
let p_ref = &p;
negate(*p_ref);
// error: cannot move out of `*p_ref` which is behind a shared reference
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// now `Point` is `Copy`
#[derive(Clone, Copy)]
struct Point {
x: f64,
y: f64,
}
fn negate(p: Point) -> Point {
Point {
x: -p.x,
y: -p.y,
}
}
fn main() {
let p = Point { x: 1.0, y: 3.0 };
let p_ref = &p;
negate(*p_ref); // ...and now this works
}

闭包(Closure)是实现了FnFnMutFnOnce类型的函数。并且带有捕获的上下文。

参数是以逗号分隔的名称列表,在|之中。它们不需要大括号,除非你有多行语句:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn for_each_planet<F>(f: F)
where F: Fn(&'static str)
{
f("Earth");
f("Mars");
f("Jupiter");
}
fn main() {
for_each_planet(|planet| println!("Hello, {}", planet));
}
// prints:
// Hello, Earth
// Hello, Mars
// Hello, Jupiter

借用规则同样适用:

1
2
3
4
5
6
7
8
9
10
11
12
13
fn for_each_planet<F>(f: F)
where F: Fn(&'static str)
{
f("Earth");
f("Mars");
f("Jupiter");
}
fn main() {
let greeting = String::from("Good to see you");
for_each_planet(|planet| println!("{}, {}", greeting, planet));
// our closure borrows `greeting`, so it cannot outlive it
}

比如下面的代码就不工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fn for_each_planet<F>(f: F)
where F: Fn(&'static str) + 'static // `F` must now have "'static" lifetime
{
f("Earth");
f("Mars");
f("Jupiter");
}
fn main() {
let greeting = String::from("Good to see you");
for_each_planet(|planet| println!("{}, {}", greeting, planet));
// error: closure may outlive the current function, but it borrows
// `greeting`, which is owned by the current function
}

但是下面的代码就可以:

1
2
3
4
5
6
fn main() {
let greeting = String::from("You're doing great");
for_each_planet(move |planet| println!("{}, {}", greeting, planet));
// `greeting` is no longer borrowed, it is *moved* into
// the closure.
}

FnMut需要可变地借用去调用,所以它同时只能调用一次。

下面的代码合法:

1
2
3
4
5
6
7
8
9
10
11
fn foobar<F>(f: F)
where F: Fn(i32) -> i32
{
println!("{}", f(f(2)));
}
fn main() {
foobar(|x| x * 2);
}
// output: 8

下面的代码不合法:

1
2
3
4
5
6
7
8
9
10
fn foobar<F>(mut f: F)
where F: FnMut(i32) -> i32
{
println!("{}", f(f(2)));
// error: cannot borrow `f` as mutable more than once at a time
}
fn main() {
foobar(|x| x * 2);
}

然后下民的代码有合法了:

1
2
3
4
5
6
7
8
9
10
11
12
fn foobar<F>(mut f: F)
where F: FnMut(i32) -> i32
{
let tmp = f(2);
println!("{}", f(tmp));
}
fn main() {
foobar(|x| x * 2);
}
// output: 8

之所以有FnMut类似,是因为有些闭包会可变地借用本地变量:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn foobar<F>(mut f: F)
where F: FnMut(i32) -> i32
{
let tmp = f(2);
println!("{}", f(tmp));
}
fn main() {
let mut acc = 2;
foobar(|x| {
acc += 1;
x * acc
});
}
// output: 24

这些闭包不能传给期望Fn类型的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fn foobar<F>(f: F)
where F: Fn(i32) -> i32
{
println!("{}", f(f(2)));
}
fn main() {
let mut acc = 2;
foobar(|x| {
acc += 1;
// error: cannot assign to `acc`, as it is a
// captured variable in a `Fn` closure.
// the compiler suggests "changing foobar
// to accept closures that implement `FnMut`"
x * acc
});
}

FnOnce闭包只会被调用一次。因为一些闭包会把move进来的变量在返回值中move out:

1
2
3
4
5
6
7
8
9
10
11
12
13
fn foobar<F>(f: F)
where F: FnOnce() -> String
{
println!("{}", f());
}
fn main() {
let s = String::from("alright");
foobar(move || s);
// `s` was moved into our closure, and our
// closures moves it to the caller by returning
// it. Remember that `String` is not `Copy`.
}

这是自然限制的,因为FnOnce闭包在调用的时候需要被move:

1
2
3
4
5
6
7
fn foobar<F>(f: F)
where F: FnOnce() -> String
{
println!("{}", f());
println!("{}", f());
// error: use of moved value: `f`
}

而且你确保使用move调用闭包,它依然是非法的:

1
2
3
4
5
6
fn main() {
let s = String::from("alright");
foobar(move || s);
foobar(move || s);
// use of moved value: `s`
}

但是下面的代码就是合法的:

1
2
3
4
5
fn main() {
let s = String::from("alright");
foobar(|| s.clone());
foobar(|| s.clone());
}

下面是有两个参数的闭包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
fn foobar<F>(x: i32, y: i32, is_greater: F)
where F: Fn(i32, i32) -> bool
{
let (greater, smaller) = if is_greater(x, y) {
(x, y)
} else {
(y, x)
};
println!("{} is greater than {}", greater, smaller);
}
fn main() {
foobar(32, 64, |x, y| x > y);
}

下面是忽略了这两个参数的闭包:

1
2
3
fn main() {
foobar(32, 64, |_, _| panic!("Comparing is futile!"));
}

下面是一个唱“滴答”的闭包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn countdown<F>(count: usize, tick: F)
where F: Fn(usize)
{
for i in (1..=count).rev() {
tick(i);
}
}
fn main() {
countdown(3, |i| println!("tick {}...", i));
}
// output:
// tick 3...
// tick 2...
// tick 1...

下面是一个马桶闭包:

1
2
3
fn main() {
countdown(3, |_| ());
}

|_| ()看起来像不像一个🚽?

任意的迭代的东西都可以放在for in循环中。

前面我们已经看到了range,也可以使用for in操作Vec:

1
2
3
4
5
fn main() {
for i in vec![52, 49, 21] {
println!("I like the number {}", i);
}
}

或者处理slice:

1
2
3
4
5
6
7
8
9
10
fn main() {
for i in &[52, 49, 21] {
println!("I like the number {}", i);
}
}
// output:
// I like the number 52
// I like the number 49
// I like the number 21

或者是一个实际的迭代器:

1
2
3
4
5
6
7
8
9
10
11
12
13
fn main() {
// note: `&str` also has a `.bytes()` iterator.
// Rust's `char` type is a "Unicode scalar value"
for c in "rust".chars() {
println!("Give me a {}", c);
}
}
// output:
// Give me a r
// Give me a u
// Give me a s
// Give me a t

即使迭代器的元素被加上了过滤、映射、展开等操作:

1
2
3
4
5
6
7
8
9
10
11
12
fn main() {
for c in "sHE'S brOKen"
.chars()
.filter(|c| c.is_uppercase() || !c.is_ascii_alphabetic())
.flat_map(|c| c.to_lowercase())
{
print!("{}", c);
}
println!();
}
// output: he's ok

你可以在一个函数中返回一个闭包:

1
2
3
4
5
6
7
8
9
10
11
12
13
fn make_tester(answer: String) -> impl Fn(&str) -> bool {
move |challenge| {
challenge == answer
}
}
fn main() {
// you can use `.into()` to perform conversions
// between various types, here `&'static str` and `String`
let test = make_tester("hunter2".into());
println!("{}", test("******"));
println!("{}", test("hunter2"));
}

甚至你可以move函数的参数的引用到闭包中返回:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
fn make_tester<'a>(answer: &'a str) -> impl Fn(&str) -> bool + 'a {
move |challenge| {
challenge == answer
}
}
fn main() {
let test = make_tester("hunter2");
println!("{}", test("*******"));
println!("{}", test("hunter2"));
}
// output:
// false
// true

省略生命周期名称:

1
2
3
4
5
fn make_tester(answer: &str) -> impl Fn(&str) -> bool + '_ {
move |challenge| {
challenge == answer
}
}

好了,半小时到了,通过本次学习,你应该能通过线上大部分的rust代码。

写rust代码和读rust又有很大的不同。一方面你不是在读一个问题的解决访问,而是正在解决它;另一方面Rust编译器会给你很大的帮助。

对于上面非法的rust代码,rustc总是会给你清楚的错误提示和富有洞察力的建议。

如果提示缺失,编译器组欢迎你提出来。

如果需要更多的Rust学习资料,你可以访问下面的资源:

我也会写一些关于rust的博文和tweet。

阅读愉快。

blablabla一堆感谢。