读Rust By Example(3)

自定类型

Rust的自定类型主要由两个关键字组成:

  • struct: 定义一个结构。
  • enum: 定义一个枚举。

常量可以由conststatic关键字创建。

结构

struct关键字可以创造出三种结构类型:

  • 元组结构,总的来说是根据元组来命名
  • 经典C结构
  • 单元结构体,不带字段,在泛型中很有用

样例

// 单元结构
struct Nil;

// 元组结构
struct Pair(i32, f32);

// 含有两个字段的结构
struct Point {  
    x: f32,
    y: f32,
}

// 结构可被其他结构重用
#[allow(dead_code)]
struct Rectangle {  
    p1: Point,
    p2: Point,
}

fn main() {  
    // 实例化一个 `Point`
    let point: Point = Point { x: 0.3, y: 0.4 };

    // 访问 point 的字段
    println!("point coordinates: ({}, {})", point.x, point.y);

    // 用 `let` 绑定析构 point
    let Point { x: my_x, y: my_y } = point;

    let _rectangle = Rectangle {
        // 结构的实例化也是表达式
        p1: Point { x: my_y, y: my_x },
        p2: point,
    };

    // 实例化一个单元结构
    let _nil = Nil;

    // 实例化一个元组结构体
    let pair = Pair(1, 0.1);

    // 访问元组结构的字段
    println!("pair contains {:?} and {:?}", pair.0, pair.1);

    // 析构一个元组结构体
    let Pair(integer, decimal) = pair;

    println!("pair contains {:?} and {:?}", integer, decimal);
}

练习: * 增加一个rect_area函数来计算长方形的面积,尝试使用嵌套析构方式

解答:

use std::fmt;

struct Point {  
    x: f32,
    y: f32,
}

struct Rectangle {  
    p1: Point,
    width: f32,
    height: f32,
}

fn rect_area(rect: Rectangle) -> f32{  
    let Rectangle{p1:point, width: x, height: y} = rect;
    x * y
}

fn main(){  
    let p = Point{x: 3.0,y: 2.0};
    let rect = Rectangle{p1: p, width: 3.0, height: 2.0};
    println!("{}", rect_area(rect));
}
  • 增加一个square函数,接受参数是一个Point和一个f32,并返回一个Rectangle的信息,包括左下角的点,以及长和宽的浮点数值

解答:

待解答

枚举

enum关键字允许创建包含多种变量的类型,如果一个变量对struct有效,那么它对enum也是有效的。

样例:

// 用于隐藏未使用变量警告的属性
#![allow(dead_code)]

// 创建一个 `enum` 来对某人分类. 
// 注意命名和类型的信息是如何一起明确规定变量的
// 没两者都不相同且相互独立
enum Person {  
    // 一个 `enum` 也可能是个 `unit-like`(类单元结构体)
    Engineer,
    Scientist,
    // 或者像一个元组结构体
    Height(i32),
    Weight(i32),
    // 或者像普通结构体
    Info { name: String, height: i32 }
}

// 以 `Person` 枚举作为参数的函数,无返回值
fn inspect(p: Person) {  
    // `enum` 必须覆盖所有的情形(无可辩驳)
    // 要使用 `match` 分支覆盖所有类型
    match p {
        Person::Engineer  => println!("Is an engineer!"),
        Person::Scientist => println!("Is a scientist!"),
        // 从 `enum`内部析构 `i`
        Person::Height(i) => println!("Has a height of {}.", i),
        Person::Weight(i) => println!("Has a weight of {}.", i),
        // 将 `Info` 析构成 `name` and `height`.
        Person::Info { name, height } => {
            println!("{} is {} tall!", name, height);
        },
    }
}

fn main() {  
    let person   = Person::Height(18);
    let amira    = Person::Weight(10);
    // `to_owned()` 从一个字符串切片创建一个具有所有权的 `String`
    let dave     = Person::Info { name: "Dave".to_owned(), height: 72 };
    let rebecca  = Person::Scientist;
    let rohan    = Person::Engineer;

    inspect(person);
    inspect(amira);
    inspect(dave);
    inspect(rebecca);
    inspect(rohan);
}
使用use

使用 use 声明,就不必手动加上作用域了。

样例:

// 用于隐藏未使用变量警告的属性
#![allow(dead_code)]

enum Status {  
    Rich,
    Poor,
}

enum Work {  
    Civilian,
    Soldier,
}

fn main() {  
    // 明确地 `use` 各个名称使他们直接可用而不需要手动加上作用域
    use Status::{Poor, Rich};
    // 自动地 `use` `Work` 内部的各个名称
    use Work::*;

    // 相当于 `Status::Poor`.
    let status = Poor;
    // 相当于 `Work::Civilian`.
    let work = Civilian;

    match status {
        // 不需要作用域是由于上面已经 `use` 了
        Rich => println!("The rich have lots of money!"),
        Poor => println!("The poor have no money..."),
    }

    match work {
        // Note again the lack of scoping.
        Civilian => println!("Civilians work!"),
        Soldier  => println!("Soldiers fight!"),
    }
}
类C

enum也可以用于类C枚举。

样例:

// 用于隐藏未使用变量警告的属性
#![allow(dead_code)]

// 带有隐式辨别值 (从0开始)
enum Number {  
    Zero,
    One,
    Two,
}

// 带有显式辨别值
enum Color {  
    Red = 0xff0000,
    Green = 0x00ff00,
    Blue = 0x0000ff,
}

fn main() {  
    // `enums` can be cast as integers.
    println!("zero is {}", Number::Zero as i32);
    println!("one is {}", Number::One as i32);

    println!("roses are #{:06x}", Color::Red as i32);
    println!("violets are #{:06x}", Color::Blue as i32);
}
Testcase: 链表

枚举的一个常用方法就是创建链表

样例:

use List::*;

enum List {  
    // Cons: 元组结构体,包含一个元素及指向下个节点的指针
    Cons(u32, Box<List>),
    // Nil: 用于标注链表终结的节点
    Nil,
}

// 添加一个枚举的方法
impl List {  
    // 创建空表
    fn new() -> List {
        // `Nil` 拥有 `List` 类型
        Nil
    }

    // 处理一个列表,得到一个头部带上一个新元素的同样类型的列表并返回此值
    fn prepend(self, elem: u32) -> List {
        // `Cons` 仍拥有 `List` 类型
        Cons(elem, Box::new(self))
    }

    // 返回表长
    fn len(&self) -> u32 {
        // `self` 需要被分支覆盖,因为这个方法取决于 `self` 的变量
        // `self` 为 `&List` 类型, `*self` 为 `List` 类型
        // 一个具体的 `T` 类型要参考引用 `&T` 的匹配
        match *self {
            // 不能得到tail的所有权, 因为 `self` 是借用的
            // 而是得到tail的引用
            Cons(_, ref tail) => 1 + tail.len(),
            // 基本情况:空链表
            Nil => 0
        }
    }

    // 将表以字符串(堆分配的)的形式返回
    fn stringify(&self) -> String {
        match *self {
            Cons(head, ref tail) => {
                // `format!` 类似于 `print!`, 但返回堆分配的字符串而不是打印在终端上
                format!("{}, {}", head, tail.stringify())
            },
            Nil => {
                format!("Nil")
            },
        }
    }
}

fn main() {  
    // Create an empty linked list
    let mut list = List::new();

    // Append some elements
    list = list.prepend(1);
    list = list.prepend(2);
    list = list.prepend(3);

    // Show the final state of the list
    println!("linked list has length: {}", list.len());
    println!("{}", list.stringify());
}

常量

Rust有两种完全不同的常量,两者都需要显式的类型定义。

  • const: 不可变量(通常情况下)
  • static: 具有'static生命周期的可能发生改变的变量

一个特例是string原始类型,可以被直接分配为static变量,由于其类型标记为&'static str具有'static的生命周期。除此之外其他的类型都必须显式指定。这似乎是无关紧要的,因为所需的显式标记会隐藏差异。

样例

// 全局变量将声明在所有作用于外
static LANGUAGE: &'static str = "Rust";  
const  THRESHOLD: i32 = 10;

fn is_big(n: i32) -> bool {  
    // 在函数中访问常量
    n > THRESHOLD
}

fn main() {  
    let n = 16;

    // 在主线程中访问常量
    println!("This is {}", LANGUAGE);
    println!("The threshold is {}", THRESHOLD);
    println!("{} is {}", n, if is_big(n) { "big" } else { "small" });

    // Error! Cannot modify a `const`.
    // THRESHOLD = 5;
    // FIXME ^ Comment out this line
}