Rust 是一个系统编程语言,它注重于三个方面:安全,速度和并发性。为了实现这些目标,它没有采用垃圾回收机制(GC)。
println!()
这一行。这是一个 Rust 宏,是 Rust 元编程的关键所在。相反如果我们调用一个函数的话,它应该看起来像这样:println()
(没有 !)。当看到符号 !
的时候,就代表调用了一个宏而不是一个普通的函数。
Cargo
Cargo 是 Rust 的构建系统和包管理工具,同时 Rustacean 们使用 Cargo 来管理它们的 Rust 项目。Cargo 负责三个工作:构建你的代码,下载你代码依赖的库并编译这些库。
Cargo 编译你的程序所需要知道的三个配置:包的名字,版本,和作者。
语法
模式(Patterns)
在许多语言中,这叫做变量。不过 Rust 的变量绑定有一些不同的巧妙之处。例如let
语句的左侧是一个“模式”,而不仅仅是一个变量。这意味着我们可以这样写:
let (x, y) = (1, 2);
在这个语句被计算后,x
将会是1,而y
将会是2。
可变性(Mutability)
绑定默认是不可变的(immutable)。下面的代码将不能编译:
let x = 5;
x = 10;
不止一个理由使得绑定默认不可变的,不过我们可以通过一个 Rust 的主要目标来理解它:安全。
初始化绑定(Initializing bindings)
Rust 变量绑定有另一个不同于其它语言的方面:绑定要求在可以使用它之前必须初始化。
作用域和隐藏(Scope and shadowing)
表达式 VS 语句
表达式返回一个值,而语句不是。x + 1;
语句不返回一个值。会报错
fn add_one(x: i32) -> i32 {
x + 1;
}
x + 1返回一个值。不会报错。
fn add_one(x: i32) -> i32 {
x + 1
}
原生类型
切片
一个切片(slice)是一个数组的引用(或者“视图”)。它有利于安全,有效的访问数组的一部分而不用进行拷贝。
在底层,slice 代表一个指向数据开始的指针和一个长度。
你可以用一个&
和[]
的组合从多种数据类型创建一个切片。带有一个范围的[]
,允许你定义切片的长度:
let a = [0, 1, 2, 3, 4];
let complete = &a[..]; // A slice containing all of the elements in `a`.
let middle = &a[1..4]; // A slice of `a`: only the elements `1`, `2`, and `3`.
元组(Tuples)
元组(tuples)是固定大小的有序列表,元组是异质的:这个元组中有一个i32
和一个&str
。如下:
let x = (1, "hello");
这是一个长度为 2 的元组,由括号和逗号组成。下面也是同样的元组,不过注明了数据类型:
let x: (i32, &str) = (1, "hello");
你可以通过一个解构let(destructuring let)访问元组中的字段。下面是一个例子:
let (x, y, z) = (1, 2, 3);
println!("x is {}", x);
你也可以用索引语法访问一个元组的字段:
let tuple = (1, 2, 3);
let x = tuple.0;
let y = tuple.1;
let z = tuple.2;
println!("x is {}", x);
就像数组索引,它从0
开始,不过也不像数组索引,它使用.
,而不是[]
。
Vectors
相对数组长度可变。
必须用usize
类型的值来索引:
let v = vec![1, 2, 3, 4, 5];
let i: usize = 0;
let j: i32 = 0;
// Works:
v[i];
// Doesn’t:
v[j];
注意:你不能在使用 vector 的所有权遍历之后再次遍历它。你可以使用它的引用多次遍历 vector。
例如,下面的代码不能编译。
let v = vec![1, 2, 3, 4, 5];
for i in v {
println!("Take ownership of the vector and its element {}", i);
}
for i in v {
println!("Take ownership of the vector and its element {}", i);
}
而如下代码则可以完美运行:
let v = vec![1, 2, 3, 4, 5];
for i in &v {
println!("This is a reference to {}", i);
}
for i in &v {
println!("This is a reference to {}", i);
}
所有权,有点类似c++的智能指针unique_ptr,引用和借用有点类似C++的引用概念,只有与引用(例如一个包含引用的struct
)相关的变量才需要生命周期。
impl
关键字提供了使用
方法调用语法。
我们也可以定义一个不带self
参数的关联函数。这是一个 Rust 代码中非常常见的模式:
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
fn new(x: f64, y: f64, radius: f64) -> Circle {
Circle {
x: x,
y: y,
radius: radius,
}
}
}
fn main() {
let c = Circle::new(0.0, 0.0, 2.0);
}
创建者模式(Builder Pattern)
我们说我们需要我们的用户可以创建圆,不过我们只允许他们设置他们关心的属性。否则,x
和y
将是0.0
,并且radius
将是1.0
。Rust 并没有方法重载,命名参数或者可变参数。我们利用创建者模式来代替。它看起像这样:
struct Circle {
x: f64,
y: f64,
radius: f64,
}
impl Circle {
fn area(&self) -> f64 {
std::f64::consts::PI * (self.radius * self.radius)
}
}
struct CircleBuilder {
x: f64,
y: f64,
radius: f64,
}
impl CircleBuilder {
fn new() -> CircleBuilder {
CircleBuilder { x: 0.0, y: 0.0, radius: 1.0, }
}
fn x(&mut self, coordinate: f64) -> &mut CircleBuilder {
self.x = coordinate;
self
}
fn y(&mut self, coordinate: f64) -> &mut CircleBuilder {
self.y = coordinate;
self
}
fn radius(&mut self, radius: f64) -> &mut CircleBuilder {
self.radius = radius;
self
}
fn finalize(&self) -> Circle {
Circle { x: self.x, y: self.y, radius: self.radius }
}
}
fn main() {
let c = CircleBuilder::new()
.x(1.0)
.y(2.0)
.radius(2.0)
.finalize();
println!("area: {}", c.area());
println!("x: {}", c.x);
println!("y: {}", c.y);
}
由 Rust 标准库提供的特殊 trait,Drop
。Drop
trait 提供了一个当一个值离开作用域后运行一些代码的方法。例如:
struct HasDrop;
impl Drop for HasDrop {
fn drop(&mut self) {
println!("Dropping!");
}
}
fn main() {
let x = HasDrop;
// Do stuff.
} // `x` goes out of scope here.
当在main()
的末尾x
离开作用域的时候,Drop
的代码将会执行。Drop按照后进先出的顺序丢弃资源。