Cargo

  1. cargo new guessing_game --bin
  2. cargo build
  3. cargo run
  4. cargo build --release

生成一个新的工程,--bin 表示这个是一个binary工程不是一个库
crates.io 仓库,可以查找之后加到dependency中
例如如果添加了一个regex库
那么可以去使用他

  1. use regex::Regex;
  2. fn main() {
  3. let re = Regex::new(r"^\d{4}-\d{2}-\d{2}$").unwrap();
  4. println!("Did our date match? {}", re.is_match("2014-01-01"));
  5. }
  • Cargo.toml and Cargo.lock are stored in the root of your package (package root).
  • Source code goes in the src directory.
  • The default library file is src/lib.rs.
  • The default executable file issrc/main.rs.
    • Other executables can be placed in src/bin/.
  • Benchmarks go in the benches directory.
  • Examples go in the examples directory.
  • Integration tests go in the tests directory.

    运行examples

    1. cargo build --example binding_over_turn
    2. cargo run --example exname -- --exoption exarg1 exarg2

    会运行工程目录里的examples里面的binding_over_turn.rs

    Target Selection

    When no target selection options are given, cargo run will run the binary target. If there are multiple binary targets, you must pass a target flag to choose one. Or, the default-run field may be specified in the [package] section of Cargo.toml to choose the name of the binary to run by default.

  • --bin _name_Run the specified binary.

  • --example _name_Run the specified example.

    Package Selection

    By default, the package in the current working directory is selected. The -p flag can be used to choose a different package in a workspace.

  • -p spec

  • --package _spec_The package to run. See cargo-pkgid(1) for the SPEC format.

crate and package

A package can have multiple binary crates by placing files in the src/bin directory: each file will be a separate binary crate.

module模块

A module without a body is loaded from an external file
在文件系统中,目录结构往往以斜杠在路径字符串中表示对象的位置,Rust 中的路径分隔符是 ::
路径分为绝对路径和相对路径。绝对路径从 crate 关键字开始描述。相对路径从 self 或 super 关键字或一个标识符开始描述。例如:

  1. crate::nation::government::govern();

是描述 govern 函数的绝对路径,相对路径可以表示为:

  1. nation::government::govern();

访问权限

Rust 中有两种简单的访问权:公共(public)和私有(private)。
默认情况下,如果不加修饰符,模块中的成员访问权将是私有的。
如果想使用公共权限,需要使用 pub 关键字。
对于私有的模块,只有在与其平级的位置或下级的位置才能访问,不能从其外部访问。

String

  1. use std::io;
  2. fn main() {
  3. println!("Guess the number!");
  4. println!("Please input your guess.");
  5. let mut guess = String::new();
  6. io::stdin().read_line(&mut guess)
  7. .expect("Failed to read line");
  8. println!("You guessed: {}", guess);
  9. }
  1. let foo = 5; // immutable.
  2. let mut bar = 5; // mutable

String是一个字符串类型,由标准库提供。String是一个可增长的,UTF-8编码的文本。
String::new() 语法用了::因为它是一个特定类型的”关联函数“。这就是说,它与String自身关联,而不是与一个特定的String实例关联。一些语言管这叫一个“静态方法”。

  1. println!("You guessed: {}", guess);

这打印出我们保存输入的字符串。{}是一个占位符,所以我们传递guess作为一个参数。如果我们有多个{},我们应该传递多个参数:

Shadow

  1. let mut gusss: string = "xxxx"
  2. let guess: u32 = guess.trim().parse()
  3. .expect("Please type a number!");
  4. let mut x: i32 = 1;
  5. x = 7;
  6. let x = x; // `x` is now immutable and is bound to `7`
  7. let y = 4;
  8. let y = "I can also be bound to text!"; // `y` is now of a different type

稍等,我认为我们已经用过了一个guess?确实,不过 Rust 允许我们用新值“遮盖(shadow)”之前的guess。这在这种具体的情况中经常被用到,guess开始是一个String,不过我们想要把它转换为一个u32。遮盖(Shadowing)让我们重用guess名字,而不是强迫我们想出两个独特的像guess_strguess,或者别的什么。

Unused var

  1. let guess: u32 = guess.trim().parse()
  2. let _guess: u32 = guess.trim().parse() //表示guess不适用,跟go _一样
  3. let guess: &str = "xxxx";
  4. let guess = "xxx"; //不指定类型

忽略错误

  1. let guess: u32 = match guess.trim().parse() {
  2. Ok(num) => num,
  3. Err(_) => continue,
  4. };//捕捉panic
  5. let guess: u32 = guess.trim().parse()
  6. .expect("Please type a number!");//会panic

这是你如何大体上从“错误就崩溃”移动到“确实处理错误”,通过从expect()切换到一个match语句。parse()返回的Result就是一个像Ordering一样的枚举,不过在这个例子中,每个变量有一些数据与之相关:Ok是一个成功,而Err是一个失败。每一个都包含更多信息:成功的解析为整型,或一个错误类型。在这个例子中,我们matchOk(num),它设置了Ok内叫做num的值,接着在右侧返回它。在Err的情况下,我们并不关心它是什么类型的错误,所以我们仅仅使用_而不是一个名字。这忽略错误,并continue造成我们进行loop的下一次迭代。

表达式 VS 语句

Rust 主要是一个基于表达式的语言。只有两种语句,其它的一切都是表达式。

然而这又有什么区别呢?表达式返回一个值,而语句不是。这就是为什么这里我们以“不是所有控制路径都返回一个值”结束:x + 1;语句不返回一个值。Rust 中有两种类型的语句:“声明语句”和“表达式语句”。其余的一切是表达式。

  1. let mut y = 5;
  2. let x = (y = 6); // `x` has the value `()`, not `6`.
  1. fn add_one(x: i32) -> i32 {
  2. x + 1
  3. }

我们的函数声称它返回一个i32,但是带上个分号,它就会返回一个()。Rust意识到这大概不是我们想要的,并在之前我们看到的错误中建议去掉分号。

发散函数(Diverging functions)

Rust有些特殊的语法叫“发散函数”,这些函数并不返回:

  1. fn diverges() -> ! {
  2. panic!("This function never returns!");
  3. }

panic!是一个宏,类似我们已经见过的println!()。与println!()不同的是,panic!()导致当前的执行线程崩溃并返回指定的信息。因为这个函数会崩溃,所以它不会返回,所以它拥有一个类型!,它代表“发散”。

函数指针

我们也可以创建指向函数的变量绑定:

  1. let f: fn(i32) -> i32;

f是一个指向一个获取i32作为参数并返回i32的函数的变量绑定。

类型

数字

这里有一个不同数字类型的列表,以及它们在标准库中的文档:

数组

像很多编程语言一样,Rust有用来表示数据序列的列表类型。最基本的是数组,一个定长相同类型的元素列表。数组默认是不可变的。

  1. let a = [1, 2, 3]; // a: [i32; 3]
  2. let mut m = [1, 2, 3]; // m: [i32; 3]

数组的类型是[T; N]。我们会在泛型部分的时候讨论这个T标记。N是一个编译时常量,代表数组的长度。

有一个可以将数组中每一个元素初始化为相同值的简写。在这个例子中,a的每个元素都被初始化为0

  1. let a = [0; 20]; // a: [i32; 20]

切片(Slices)

一个切片slice)是一个数组的引用(或者“视图”)。它有利于安全,有效的访问数组的一部分而不用进行拷贝。比如,你可能只想要引用读入到内存的文件中的一行。原理上,片段并不是直接创建的,而是引用一个已经存在的变量。片段有预定义的长度,可以是可变也可以是不可变的。
在底层,slice 代表一个指向数据开始的指针和一个长度。

切片语法(Slicing syntax)

你可以用一个&[]的组合从多种数据类型创建一个切片。&表明切片类似于引用,这个我们会在本部分的后面详细介绍。带有一个范围的[],允许你定义切片的长度:

  1. let a = [0, 1, 2, 3, 4];
  2. let complete = &a[..]; // A slice containing all of the elements in `a`.
  3. let middle = &a[1..4]; // A slice of `a`: only the elements `1`, `2`, and `3`.

片段拥有&[T]类型。当我们涉及到泛型时会讨论这个T
你可以在标准库文档中找到更多关于slices的文档。

元组(Tuples)

元组(tuples)是固定大小的有序列表。如下:

  1. let x = (1, "hello");

这是一个长度为 2 的元组,由括号和逗号组成。下面也是同样的元组,不过注明了数据类型:

  1. let x: (i32, &str) = (1, "hello");

如你所见,元组的类型跟元组看起来很像,只不过类型取代的值的位置。细心的读者可能会注意到元组是异质的:这个元组中有一个i32和一个&str。在系统编程语言中,字符串要比其它语言中来的复杂。现在,可以认为&str是一个字符串片段string slice),我们马上会讲到它。
你可以把一个元组赋值给另一个,如果它们包含相同的类型和数量。当元组有相同的长度时它们有相同的数量。

  1. let mut x = (1, 2); // x: (i32, i32)
  2. let y = (2, 3); // y: (i32, i32)
  3. x = y;

你可以通过一个解构letdestructuring let)访问元组中的字段。下面是一个例子:

  1. let (x, y, z) = (1, 2, 3);
  2. println!("x is {}", x);

还记得之前我曾经说过let语句的左侧远比一个赋值绑定强大吗?这就是证据。我们可以在let左侧写一个模式,如果它能匹配右侧的话,我们可以一次写多个绑定。这种情况下,let“解构”或“拆开”了元组,并分成了三个绑定。

这个模式是很强大的,我们后面会经常看到它。
你可以一个逗号来消除一个单元素元组和一个括号中的值的歧义:

  1. (0,); // single-element tuple
  2. (0); // zero in parentheses

元组索引(Tuple Indexing)

你也可以用索引语法访问一个元组的字段:

  1. let tuple = (1, 2, 3);
  2. let x = tuple.0;
  3. let y = tuple.1;
  4. let z = tuple.2;
  5. println!("x is {}", x);

就像数组索引,它从0开始,不过也不像数组索引,它使用.,而不是[]
你可以在标准库文档中找到更多关于tuple的文档。

IF

这些都是非常标准的情况。然而你也可以这么写:

  1. let x = 5;
  2. let y = if x == 5 {
  3. 10
  4. } else {
  5. 15
  6. }; // y: i32

你可以(或许也应该)这么写:

  1. let x = 5;
  2. let y = if x == 5 { 10 } else { 15 }; // y: i32

循环

  • loop

  • while

  • for 和go差不多

  1. for x in 0..10 {
  2. println!("{}", x); // x: i32
  3. }

在我们的例子中,0..10表达式取一个开始和结束的位置,然后给出一个含有这之间值得迭代器。当然它不包括上限值,所以我们的循环会打印09,而不是到10
更抽象的形式:

  1. for var in expression {
  2. code
  3. }

对于范围(On ranges):

  1. for (index, value) in (5..10).enumerate() {
  2. println!("index = {} and value = {}", index, value);
  3. }

输出:

  1. index = 0 and value = 5
  2. index = 1 and value = 6
  3. index = 2 and value = 7
  4. index = 3 and value = 8
  5. index = 4 and value = 9

别忘了在范围外面加上括号。

对于迭代器(On iterators):

  1. let lines = "hello\nworld".lines();
  2. for (linenumber, line) in lines.enumerate() {
  3. println!("{}: {}", linenumber, line);
  4. }

输出:

  1. 0: hello
  2. 1: world

循环标签(Loop labels)

你也许会遇到这样的情形,当你有嵌套的循环而希望指定你的哪一个breakcontinue该起作用。就像大多数语言,默认breakcontinue将会作用于最内层的循环。当你想要一个breakcontinue作用于一个外层循环,你可以使用标签来指定你的breakcontinue语句作用的循环。如下代码只会在xy都为奇数时打印他们:

  1. 'outer: for x in 0..10 {
  2. 'inner: for y in 0..10 {
  3. if x % 2 == 0 { continue 'outer; } // Continues the loop over `x`.
  4. if y % 2 == 0 { continue 'inner; } // Continues the loop over `y`.
  5. println!("x: {}, y: {}", x, y);
  6. }
  7. }

Vector

“Vector”是一个动态或“可增长”的数组,被实现为标准库类型Vec(其中<T>是一个泛型语句)。vector 总是在堆上分配数据。vector 与切片就像String&str一样。你可以使用vec!宏来创建它:

  1. let v = vec![1, 2, 3, 4, 5]; // v: Vec<i32>

(与之前使用println!宏时不一样,我们用中括号[]配合vec!。为了方便,Rust 允许使用上述各种情况。)
对于重复初始值有另一种形式的vec!

  1. let v = vec![0; 10]; // ten zeroes

vector 将它们的内容以连续的T的数组的形式存储在堆上,这意味着它们必须在编译时就知道T的大小(就是存储一个T需要多少字节)。有些类型的大小不可能在编译时就知道。为此你需要保存一个指向该类型的指针:幸好,Box类型正好适合这种情况。
另外值得注意的是必须用usize类型的值来索引

越界访问(Out-of-bounds Access)

如果尝试访问并不存在的索引:

  1. let v = vec![1, 2, 3];
  2. println!("Item 7 is {}", v[7]);

那么当前的线程会 panic并输出如下信息:

  1. thread 'main' panicked at 'index out of bounds: the len is 3 but the index is 7'

如果你想处理越界错误而不是 panic,你可以使用像getget_mut这样的方法,他们当给出一个无效的索引时返回None

  1. let v = vec![1, 2, 3];
  2. match v.get(7) {
  3. Some(x) => println!("Item 7 is {}", x),
  4. None => println!("Sorry, this vector is too short.")
  5. }

迭代

可以用for来迭代 vector 的元素。有3个版本:

  1. let mut v = vec![1, 2, 3, 4, 5];
  2. for i in &v {
  3. println!("A reference to {}", i);
  4. }
  5. for i in &mut v {
  6. println!("A mutable reference to {}", i);
  7. }
  8. for i in v {
  9. println!("Take ownership of the vector and its element {}", i);
  10. }

注意:你不能在使用 vector 的所有权遍历之后再次遍历它。你可以使用它的引用多次遍历 vector。例如,下面的代码不能编译。

  1. let v = vec![1, 2, 3, 4, 5];
  2. for i in v {
  3. println!("Take ownership of the vector and its element {}", i);
  4. }
  5. for i in v {
  6. println!("Take ownership of the vector and its element {}", i);
  7. }

而如下代码则可以完美运行:

  1. let v = vec![1, 2, 3, 4, 5];
  2. for i in &v {
  3. println!("This is a reference to {}", i);
  4. }
  5. for i in &v {
  6. println!("This is a reference to {}", i);
  7. }

所有权

对于所有的非native类型,一个复制会导致一个move, 原有变量将不可用,然而对于native类型,因为都实现了 copy trait 所以一个赋值会触发一个copy

借用

一个借用就是类似一个引用,但是不同的是,这个引用是不能改变他的值的
借用必须保证借用方和被借用方不再一个作用于,也就是变量有你没我,有我没你

&mut引用

可以改变他的值,但前提是这个被引用的值也必须是mut的。需要使用他们(星号)来访问引用的内容。

规则

Rust 中的借用有一些规则:
第一,任何借用必须位于比拥有者更小的作用域。第二,对于同一个资源(resource)的借用,以下情况不能同时出现在同一个作用域下:

  • 1 个或多个不可变引用(&T

  • 唯一 1 个可变引用(&mut T

译者注:即同一个作用域下,要么只有一个对资源 A 的可变引用(&mut T),要么有 N 个不可变引用(&T),但不能同时存在可变和不可变的引用

你可能注意到这些看起来很眼熟,虽然并不完全一样,它类似于数据竞争的定义:

当 2 个或更多个指针同时访问同一内存位置,当它们中至少有 1 个在写,同时操作并不是同步的时候存在一个“数据竞争”

通过引用,你可以拥有你想拥有的任意多的引用,因为它们没有一个在写。如果你在写,并且你需要2个或更多相同内存的指针,则你只能一次拥有一个&mut。这就是Rust如何在编译时避免数据竞争:如果打破规则的话,我们会得到错误。
在记住这些之后,让我们再次考虑我们的例子。

当引用在它引用的变量之前声明会导致类似的问题。这是因为同一作用域中的资源以他们声明相反的顺序被释放:

  1. let y: &i32;
  2. let x = 5;
  3. y = &x;
  4. println!("{}", y);

为了解释这些,我们不得不回到Rust指导哲学的核心,内存安全,和Rust用以保证它的机制,所有权系统,和更具体的借用

你可以拥有这两种类型借用的其中一个,但不能同时拥有:

  • 拥有 1 个或多个不可变引用(&T)

  • 只有 1 个可变引用(&mut T)

生命周期

'a读作“生命周期 a”。技术上讲,每一个引用都有一些与之相关的生命周期,不过编译器在通常情况让你可以省略(也就是,省略,查看下面的生命周期省略)它们。在我们讲到它之前,让我们看看一个显式生命周期的例子:

  1. fn bar<'a>(...)

之前我们讨论了一些函数语法,不过我们并没有讨论函数名后面的<>。一个函数可以在<>之间有“泛型参数”,生命周期也是其中一种。我们在本书的后面讨论其他类型的泛型。不过现在让我们着重看生命周期。
这个生命周期的语法也表明了 bar 的生命周期不能超出 'a 的周期
我们用<>声明了生命周期。这是说bar有一个生命周期'a。如果我们有两个拥有不同生命周期的引用参数,它应该看起来像这样:

  1. fn bar<'a, 'b>(...)

接着在我们的参数列表中,我们使用了我们命名的生命周期:

  1. ...(x: &'a i32)

如果我们想要一个&mut引用,我们这么做:

  1. ...(x: &'a mut i32)

如果你对比一下&mut i32&'a mut i32,他们是一样的,只是后者在&mut i32之间夹了一个'a生命周期。&mut i32读作“一个i32的可变引用”,而&'a mut i32读作“一个带有生命周期’a的i32的可变引用”。

例子: 如下例子表示y的生命周期要比failed_borrow要长,所以y会在整个函数执行之后释放,但是_x已经先于他释放,所以会失败

  1. // 不带参量的函数,不过有一个生命周期参量 `'a`。
  2. fn failed_borrow<'a>() {
  3. let _x = 12;
  4. // 报错:`_x` 存活时间长度不够(`_x` does not live long enough)
  5. let y: &'a i32 = &_x;
  6. // 尝试使用生命周期 `'a` 作为函数内部的显式类型标注将导致失败,因为
  7. // `&_x` 的生命周期比 `y` 的短。短生命周期不能强制转换成长生命周期。
  8. }

impl

让我们在Foo中实现一个方法:

  1. struct Foo<'a> {
  2. x: &'a i32,
  3. }
  4. impl<'a> Foo<'a> {
  5. fn x(&self) -> &'a i32 { self.x }
  6. }
  7. fn main() {
  8. let y = &5; // This is the same as `let _y = 5; let y = &_y;`.
  9. let f = Foo { x: y };
  10. println!("x is: {}", f.x());
  11. }

如你所见,我们需要在impl行为Foo声明一个生命周期。我们重复了'a两次,就像在函数中:impl<'a>定义了一个生命周期'a,而Foo<'a>使用它。

‘static

叫做static的生命周期是特殊的。它代表某样东西具有横跨整个程序的生命周期。大部分 Rust 程序员当他们处理字符串时第一次遇到'static

  1. let x: &'static str = "Hello, world.";

基本字符串是&'static str类型的因为它的引用一直有效:它们被写入了最终库文件的数据段。另一个例子是全局量:

  1. static FOO: i32 = 5;
  2. let x: &'static i32 = &FOO;

它在二进制文件的数据段中保存了一个i32,而x是它的一个引用。

结构体

Rust 在语言级别不支持字段可变性,所以你不能像这么写:

  1. struct Point {
  2. mut x: i32, // This causes an error.
  3. y: i32,
  4. }

更新语法(Update syntax)

一个包含..struct表明你想要使用一些其它结构体的拷贝的一些值。例如:

  1. struct Point3d {
  2. x: i32,
  3. y: i32,
  4. z: i32,
  5. }
  6. let mut point = Point3d { x: 0, y: 0, z: 0 };
  7. point = Point3d { y: 1, .. point };

TIP

  • 如果你想要更多信息,你可以设定RUST_BACKTRACE环境变量来获取 backtrace
  • mod 中fn 默认是 private的

Ref

  1. // 赋值语句中左边的 `ref` 关键字等价右边的 `&` 符号。
  2. let ref ref_c1 = c;
  3. let ref_c2 = &c;

Mut

  • mut 是用来修饰变量可变性的,所以是放在变量前面
  • mut 放在冒号后面,也就是用来修饰类型的时候,只是用在可变引用&mut T和裸指针*mut T上,并不能直接放在类型前面

    引用计数

    std::sync::Arc 线程安全的引用计数
    std::sync::Rc 不是线程安全的引用计数

泛型

  1. struct Wrapper<T> {
  2. value: T
  3. }
  4. impl<T> Wrapper<T> {
  5. pub fn new(value: T) -> Self {
  6. Wrapper { value }
  7. }
  8. }

trait

  1. trait AppendBar {
  2. fn append_bar(self) -> Self;
  3. }
  4. impl AppendBar for String {
  5. //可以是 &self 可以是 mut self 都能匹配和那个trait
  6. fn append_bar(mut self) -> Self {
  7. self.push_str("Bar");
  8. self
  9. }
  10. }

sized

All type parameters have an implicit bound of Sized. The special syntax ?Sized can be used to remove this bound if it’s not appropriate.

try! or ?

try!/?
是一回事,但是对于io::Result<(), Error> 不行
https://doc.rust-lang.org/std/ops/trait.Try.html