Rust Crates 源使用帮助

$HOME/.cargo/config 中添加如下内容:

1
2
3
4
5
[source.crates-io]
replace-with = 'ustc'

[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"

Rust 是一门系统级编程语言,被设计为保证内存和线程安全,并防止段错误。作为系统级编程语言,它的基本理念是 零开销抽象

Rust 通过所有权系统管理内存,编译器在编译时会根据一系列规则进行检查。在运行时,所有权系统的任何功能都不会减慢程序。

Rust 可以被归为通用、多范式、编译型、线程安全编程语言。

强调安全性、并发和内存控制。内存安全通过所有权/借用机制、生命周期、以及类型系统来达到的。

Rust是一门面向表达式的语言,也就是说大部分语句都是表达式。

Rust 使用实现(implementation)、特征(trait)和结构化类型(structured type)而不是类(class)。这与函数式语言更加接近。


Hello World

1
2
3
fn main() {
println!("hello, world");
}

fn 表示定义一个函数,main是函数名,{}里包含函数内容。

let 关键字进行变量绑定,变量前面加入 mut 关键字,变量就会成为可变绑定的变量。

1
2
let foo = 5; 			// immutable
let mut bar = 5; // mutable

例如:let mut a: f64 = 1.0;

当看到符号 ! 的时候,就代表调用了一个宏而不是一个普通的函数。

array

[T; N] 表示一个拥有 T 类型、N 个元素、大小固定的数组。

let mut array: [i32; 3] = [0; 3];


cargo

作为 rust 代码组织管理工具,cargo 提供了一系列的工具,从项目的建立、构建到测试、运行直至部署,为 rust 项目的管理提供尽可能完整的手段,与 rust 语言及其编译器 rustc 各种特性紧密结合。

Cargo 负责三个工作:构建你的代码,下载你代码依赖的库并编译这些库。

cargo new 新建项目,--bin 参数表示该项目将生成可执行文件,--lib 建立一个library:

1
2
cargo new hello_world --bin
Created binary (application) `hello_world` project

Cargo.toml

1
2
3
4
5
6
[package]
name = "hello_world"
version = "0.1.0"
authors = ["wangchenguang <wangchenguang@zhihu.com>"]

[dependencies]

cargo build 命令对项目进行编译:

1
2
3
cargo build   
Compiling hello_world v0.1.0 (file:///private/tmp/hello_world)
Finished dev [unoptimized + debuginfo] target(s) in 1.48 secs

cargo run


关键字

Rust 语言有一组保留的关键字(keywords),就像大部分语言一样,它们只能由语言本身使用。
不能使用这些关键字作为变量或函数的名称。大部分关键字有特殊的意义,一些关键字目前没有相应的功能,是为将来可能添加的功能保留的。

格式化输出

std::fmt 包含多种 traits 来控制文字显示,其中重要的两种 trait 的基本形式如下:

  • fmt::Debug: 使用 {:?} 标记。
  • fmt::Display: 使用 {} 标记。

若想用 std::fmt 的格式化 trait 打印出来,都要求实现这个 trait。

fmt::Debug 通常看起来不太简洁,自定义输出外观通常更可取。这需要手动实现 fmt::Display

打印操作由 std::fmt 里面所定义的一系列来处理,包括:

  • format! 将格式化文本写到字符串。
  • print! 将文本输出到控制台。
  • println! 将文本输出到控制台,输出结果追加一个换行符。
  • eprint! 将文本输出到标准错误。
  • eprintln! 将文本输出到标准错误,输出结果追加一个换行符。

所有权是 Rust 最为与众不同的特性,它让 Rust 无需垃圾回收即可保障内存安全。

所有运行的程序都必须管理其使用计算机内存的方式。一些语言中具有垃圾回收机制,在程序运行时不断地寻找不再使用的内存;在另一些语言中,你必须亲自分配和释放内存。Rust 则选择了第三种方式:通过所有权系统管理内存,编译器在编译时会根据一系列规则进行检查。在运行时,所有权系统的任何功能都不会减慢程序。

在像 Rust 这样的系统编程语言中,值是位于栈上还是堆上在更大程度上影响了语言的行为以及为何必须做出这样的抉择。

栈和堆都是代码在运行时可供使用的内存,但是它们的结构不同。

栈以放入值的顺序存储值并以相反顺序取出值。这也被称作 后进先出。栈中所有数据都必须占用已知且固定的大小。在编译时大小未知或大小可能变化的数据,要改为存储在堆上。堆是缺乏组织的:当向堆放入数据时,你要请求一定大小的空间。操作系统在堆的某处找到一块足够大的空位,把它标记为已使用,并返回一个表示该位置地址的 指针。这个过程称作 在堆上分配内存,简称为 分配。将数据推入栈中并不被认为是分配。因为指针的大小是已知并且固定的,你可以将指针存储在栈上,不过当需要实际数据时,必须访问指针。

入栈比在堆上分配内存要快,因为入栈时操作系统无需为存储新数据去搜索内存空间;其位置总是在栈顶。相比之下,在堆上分配内存则需要更多的工作,这是因为操作系统必须首先找到一块足够存放数据的内存空间,并接着做一些记录为下一次分配做准备。

访问堆上的数据比访问栈上的数据慢,因为必须通过指针来访问。现代处理器在内存中跳转越少就越快。处理器在处理的数据彼此较近的时候(比如在栈上)比较远的时候(比如可能在堆上)能更好的工作。在堆上分配大量的空间也可能消耗时间。当你的代码调用一个函数时,传递给函数的值和函数的局部变量被压入栈中。当函数结束时,这些值被移出栈。

跟踪哪部分代码正在使用堆上的哪些数据,最大限度的减少堆上的重复数据的数量,以及清理堆上不再使用的数据确保不会耗尽空间,这些问题正是所有权系统要处理的。一旦理解了所有权,你就不需要经常考虑栈和堆了,不过明白了所有权的存在就是为了管理堆数据,能够帮助解释为什么所有权要以这种方式工作。

所有权规则

  1. Rust 中的每一个值都有一个被称为其 所有者(owner)的变量。
  2. 值有且只有一个所有者。
  3. 当所有者(变量)离开作用域,这个值将被丢弃。

作用域(scope)是一个项(item)在程序中有效的范围。

1
2
3
4
5
{                      // s 在这里无效, 它尚未声明
let s = "hello"; // 从此处起,s 是有效的

// 使用 s
} // 此作用域已结束,s 不再有效

这里有两个重要的时间点: 当 s 进入作用域 时,它就是有效的。 这一直持续到它 离开作用域 为止。

数据类型

在 Rust 中,每一个值都属于某一个数据类型,这告诉 Rust 它被指定为何种数据,以便明确数据处理方式。
我们将看到两类数据类型子集:标量(scalar)和复合(compound)。

Rust 是 静态类型(statically typed)语言,也就是说在编译时就必须知道所有变量的类型。

标量(scalar)类型代表一个单独的值。

Rust 有四种基本的标量类型:整型、浮点型、布尔类型和字符类型。

整数 是一个没有小数部分的数字。

Rust 也有两个原生的 浮点数类型,它们是带小数点的数字。
Rust 的浮点数类型是 f32f64,分别占 32 位和 64 位。默认类型是 f64。

复合类型(Compound types)可以将多个值组合成一个类型。

Rust 有两个原生的复合类型:元组(tuple)和数组(array)。

元组是一个将多个其他类型的值组合进一个复合类型的主要方式。元组长度固定:一旦声明,其长度不会增大或缩小。

与元组不同,数组中的每个元素的类型必须相同。Rust 中的数组与一些其他语言中的数组不同,因为 Rust 中的数组是固定长度的:一旦声明,它们的长度不能增长或缩小。

结构体

结构体有 3 种类型,使用 struct 关键字来创建:

  • 元组结构体,实际上就是具名元组。
  • C 语言风格结构体。
  • 单元结构体,不带字段。

类型转换

Rust 不提供原生类型之间的隐式类型转换,但可以使用 as 关键字进行显式类型转换。

字面量

对数值字面量,只要把类型作为后缀加上去,就完成了类型说明。

比如指定字面量 42 的 类型是 i32,只需要写 42i32

无后缀的数值字面量,其类型取决于怎样使用它们。如果没有限制,编译器会对整数使用 i32,对浮点数使用 f64


Reference :

  1. Rust Primer
  2. Rust Crates 源使用帮助
  3. Rust 程序设计语言(第一版) 简体中文版
  4. RUST语言的编程范式