浏览尽可能多的 Rust 片段来熟悉并入门 Rust,而不是专注于一两个概念。
原文 A half-hour to learn Rust,有删减。
let 变量绑定:
let x;
x = 42;
let x = 42;
使用 : 显示指定变量类型,也叫类型注释(type annotation):
let x: i32; // 32 位有符号整数 signed
x = 42;
// 有 i8, i16, i32, i64, i128
// 还有 u8, u16, u32, u64, u128 表示无符号整数 unsigned
let x: i32 = 42;
下划线 _ 是一个特殊的名称,它代表扔掉一些东西:
let _ = 42; // 什么也没做
let _ = get_thing(); // 调用函数但扔掉结果
编译器不会警告以下划线开头的名称未被使用:
// 最终会用 x 但还没用,可以先这样以消除警告
let _x = 42;
Rust 有元组(tuple),可以看做是不同类型值的固定长度的集合:
let pair = ('a', 17);
pair.0; // 'a'
pair.1; // 17
给 pair 类型注释:
let pair: (char, i32) = ('a', 17);
可解构:
let (left, right) = slice.split_at(middle);
let (_, right) = slice.split_at(middle); // 扔掉一部分
注意在语句末尾写上 ; 分号,但这也意味着我们可以把语句分成多行:
let x = vec![1,2,3,4,5,6]
.iter()
.map(|x| x + 3)
.fold(0, |x, y| x + y);
使用 fn 声明一个函数:
// a void function
fn greet() {
println!("Hi!);
}
返回 32 位有符号整数的函数,使用箭头 -> 来知识其返回值类型:
fn fair_dice_roll() -> i32 {
4
}
一对括号 {} 声明一个单独作用域的块。
块也是表达式,意味着一个块计算的结果就是一个值。
所以我们可以直接写 4 而不用写 return 4。
let x = 42;
let x = { 42 }; // 都一样的
// 我们甚至可以这样定义
let x = {
let y = 1;
let z = 2;
y + z; // 块最终的计算结果
};
if 也是一个表达式:
fn fair_dice_roll() -> i32 {
if feeling_lucky {
6
} else {
4
}
}
match 也是一个表达式:
fn fair_dice_roll() -> i32 {
match feeling_lucky {
true => 6,
false => 4,
}
}
使用 . 来访问值的字段或调用值的方法:
let a = (10, 20);
a.0; // 10
let x = get_some_struct();
x.name; // "mancuoj"
let name = "mancuoj";
name.len(); // 7
双冒号 :: 与点类似,但用于命名空间。
let least = std::cmp::min(3, 8);
// std 是一个 crate 也就是一个库
// cmp 是一个 module 模块也是一个源文件
// min 是一个函数
use 指令可从其他命名空间引入模块、函数等等:
use std::cmp::min;
let least = min(7, 1);
使用大括号 {} 引入多个:
// 三者皆可
use std::cmp::min;
use std::cmp::max;
use std::cmp::{min, max};
use std::{cmp::min, cmp::max};
使用通配符 * 引入全部:
use std::cmp::*;
类型也是命名空间,可以把方法当做常规函数调用,比如这里的原生类型(primitive type)str:
let x = "mancuoj".len();
let x = str::len("mancuoj");
Rust 在每个模块的开头都插入了 use std::prelude::v1::*;,所以一些非原生类型也可以:
let v = Vec::new();
let v = std::vec::Vec::new(); // 完整路径,效果是一样的
使用 struct 声明结构体:
struct Vec2 {
x: f64, // 双精度 64 位浮点数
y: f64,
}
初始化:
let v1 = Vec2 { x: 1.0, y: 3.0 };
let v2 = Vec2 { y: 2.0, x: 4.0 }; // 顺序并不重要
let v3 = Vec2 { x: 14.0, ..v2 }; // 结构更新语法,从另一个结构初始化其余字段
let v4 = Vec2 { ..v3 };
解构(let patterns):
let v = Vec2 { x: 3.0, y: 6.0 };
let Vec2 { x, y } = v;
let Vec2 { x, .. } = v; // 丢掉 v.y
可以用于 if 语句中(if let):
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);
}
}
// Odd number: 1
// Even number: 2
用在 match 语句中(match arms):
fn print_number (n: Number) {
match n {
Number { odd: true, value } => println!("Odd number: {}", value),
Number { odd: false, value } => println!("Even number: {}", value),
}
}
match 必须要匹配其中一个 arm 分支:
fn print_number(n: Number) {
match n {
Number { value: 1, .. } => println!("One"),
Number { value: 2, .. } => println!("Two"),
Number { value, .. } => println!("{}", value), // 必须要有这最后一个 arm
}
}
// 当然也可以这样写,用 _ 包罗万物
fn print_number(n: Number) {
match n.value {
1 => println!("One"),
2 => println!("Two"),
_ => println!("{}", n.value),
}
}
在自定义的类型上声明方法:
struct Number {
odd: bool,
value: i32,
}
impl Number {
fn is_strictly_positive(self) -> bool {
self.value > 0
}
}
然后使用它:
fn main() {
let minus_two = Number {
odd: false,
value: -2,
};
println!("positive? {}", minus_two.is_strictly_positive());
// "positive? false"
}
默认情况下,变量绑定不可变,并且不能重新分配:
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
n = Number {
odd: false,
value: 22,
}; // error: cannot assign twice to immutable variable `n`
}
mut 使其可变:
fn main() {
let mut n = Number {
odd: true,
value: 17,
}
n.value = 19;
}
特质(traits)是多个类型的共同点,在类型上实现特质如下:
trait Signed {
fn is_strictly_negative(self) -> bool;
}
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()); // "true"
}
对外部类型(甚至原生类型)的特质:
impl Signed for i32 {
fn is_strictly_negative(self) -> bool {
self < 0
}
}
fn main() {
let n: i32 = -44;
println!("{}", n.is_strictly_negative()); // "true"
}
外部特质:
// 重载 `-` 运算符
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;
println!("{}", m.value); // "-987"
}
impl ... for 块用于类型,块内的 Self 代表该类型:
impl std::ops::Neg for Number {
type Output = Self;
fn neg(self) -> Self {
Self {
value: -self.value,
odd: self.odd,
}
}
}
有些 traits 是标记 markers —— 它们不是说类型 implements 了某些方法,而是说某些事情可以用类型完成。
比如说,i32 implements trait Copy:
fn main() {
let a: i32 = 15;
let b = a; // `a` is copied
let c = a; // `a` is copied again
}
// 同样以下也适用
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,不能使用:
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`
}
// 不能实现
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`
}
可以采用 immutable reference 不可变的引用 & 来实现:
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
}