Rust - 半小时快速入门

635 阅读5分钟

浏览尽可能多的 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
}