Rust 语言学习之旅(2)

22-09-29 banq

枚举
枚举允许你使用 enum 关键字创建一个新类型,该类型的值可以包含几个带标记的元素。
match 有助于确保对所有可能的枚举值进行彻底的处理,使其成为确保高质量代码的强大工具。

#![allow(dead_code)] // this line prevents compiler warnings

enum Species {
    Crab,
    Octopus,
    Fish,
    Clam
}

struct SeaCreature {
    species: Species,
    name: String,
    arms: i32,
    legs: i32,
    weapon: String,
}

fn main() {
    let ferris = SeaCreature {
        species: Species::Crab,
        name: String::from("Ferris"),
        arms: 2,
        legs: 4,
        weapon: String::from("claw"),
    };

    match ferris.species {
        Species::Crab => println!("{} is a crab",ferris.name),
        Species::Octopus => println!("{} is a octopus",ferris.name),
        Species::Fish => println!("{} is a fish",ferris.name),
        Species::Clam => println!("{} is a clam",ferris.name),
    }
}


带数据的枚举
enum 的元素可以有一个或多个数据类型,从而使其表现得像 C 语言中的联合。
当使用 match 对一个 enum 进行模式匹配时,可以将变量名称绑定到每个数据值。
enum 的内存细节:

  • 枚举数据的内存大小等于它最大元素的大小。此举是为了让所有可能的值都能存入相同的内存空间。
  • 除了元素数据类型(如果有)之外,每个元素还有一个数字值,用于表示它是哪个标签。

其他细节:
  • Rust 的 enum 也被称为标签联合 (tagged-union)
  • 把类型组合成一种新的类型,这就是人们所说的 Rust 具有 代数类型 的含义。


#![allow(dead_code)] // this line prevents compiler warnings

enum Species { Crab, Octopus, Fish, Clam }
enum PoisonType { Acidic, Painful, Lethal }
enum Size { Big, Small }
enum Weapon {
    Claw(i32, Size),
    Poison(PoisonType),
    None
}

struct SeaCreature {
    species: Species,
    name: String,
    arms: i32,
    legs: i32,
    weapon: Weapon,
}

fn main() {
    // SeaCreature's data is on stack
    let ferris = SeaCreature {
        // String struct is also on stack,
        // but holds a reference to data on heap
        species: Species::Crab,
        name: String::from("Ferris"),
        arms: 2,
        legs: 4,
        weapon: Weapon::Claw(2, Size::Small),
    };

    match ferris.species {
        Species::Crab => {
            match ferris.weapon {
                Weapon::Claw(num_claws,size) => {
                    let size_description = match size {
                        Size::Big => "big",
                        Size::Small => "small"
                    };
                    println!("ferris is a crab with {} {} claws", num_claws, size_description)
                },
                _ => println!("ferris is a crab with some other weapon")
            }
        },
        _ => println!("ferris is some other animal"),
    }
}



泛型
泛型在 Rust 中非常重要。它们用于表示可空值(即可能还没有值的变量)、错误处理、集合等等! 在本章中,我们将学习你可能将会经常使用的基本泛型知识。

泛型允许我们不完全定义一个 struct 或 enum,使编译器能够根据我们的代码使用情况,在编译时创建一个完全定义的版本。
Rust 通常可以通过查看我们的实例化来推断出最终的类型,但是如果需要帮助,你可以使用 ::<T> 操作符来显式地进行操作, 该操作符也被称为 turbofish (它是我的好朋友!)。

// 一个部分定义的结构体类型
struct BagOfHolding<T> {
    item: T,
}

fn main() {
    // 注意:通过使用泛型,我们创建了编译时才创建的类型,使代码更大
    // Turbofish 使之显式化
    let i32_bag = BagOfHolding::<i32> { item: 42 };
    let bool_bag = BagOfHolding::<bool> { item: true };
    
    // Rust 也可以推断出泛型的类型!
    let float_bag = BagOfHolding { item: 3.14 };

    // 注意:在现实生活中,不要把一袋东西放在另一袋东西里:)
    let bag_in_bag = BagOfHolding {
        item: BagOfHolding { item: "嘭!" },
    };

    println!(
        "{} {} {} {}",
        i32_bag.item, bool_bag.item, float_bag.item, bag_in_bag.item.item
    );
}


空null
在其他语言中,关键字 null 用于表示没有值。它给编程语言带来了困难,因为它使我们的程序在与变量字段交互时可能失败。
Rust 没有 null,但这并不代表我们不知道表示空的重要性!我们可以使用一个我们已经了解过的工具来简单地表示这一点。
因为缺少 null 值,这种为一个或多个替代值提供 None 替代表示的模式非常常见, 泛型有助于解决这一难题。

enum Item {
    Inventory(String),
    // None represents the absence of an item
    None,
}

struct BagOfHolding {
    item: Item,
}


Option
Rust 有一个内置的泛型枚举叫做 Option,它可以让我们不使用 null 就可以表示可以为空的值。

enum Option<T> {
    None,
    Some(T),
}

这个枚举很常见,使用关键字 Some 和 None 可以在任何地方创建其实例。

Result
Rust 有一个内置的泛型枚举叫做 Result,它可以让我们返回一个可能包含错误的值。 这是编程语言进行错误处理的惯用方法。

enum Result<T, E> {
    Ok(T),
    Err(E),
}
注意我们的泛型有多个用逗号分隔的参数化的类型。

这个枚举很常见,使用关键字 Ok 和 Err 可以在任何地方创建其实例。

main 函数有可以返回 Result 的能力!

fn do_something_that_might_fail(i: i32) -> Result<f32, String> {
    if i == 42 {
        Ok(13.0)
    } else {
        Err(String::from("this is not the right number"))
    }
}

// 主函数不返回值,但可能返回一个错误!
fn main() -> Result<(), String> {
    let result = do_something_that_might_fail(12);

    match result {
        Ok(v) => println!("found {}", v),
        Err(_e) => {
            // 优雅地处理错误
            
            // 返回一个说明发生了什么的新错误!
            return Err(String::from("something went wrong in main!"));
        }
    }

   // 注意我们在 Result Ok 中使用了一个单位值
     // 表示一切都很好
    Ok(())
}



优雅地错误处理
Result 如此常见以至于 Rust 有个强大的操作符 ? 来与之配合。 以下两个表达式是等价的:

do_something_that_might_fail()?
match do_something_that_might_fail() {
    Ok(v) => v,
    Err(e) => return Err(e),
}


Vectors
一些经常使用的泛型是集合类型。一个 vector 是可变长度的元素集合,以 Vec 结构表示。
比起手动构建,宏 vec! 让我们可以轻松地创建 vector。
Vec 有一个形如 iter() 的方法可以为一个 vector 创建迭代器,这允许我们可以轻松地将 vector 用到 for 循环中去。
内存细节:

  • Vec 是一个结构体,但是内部其实保存了在堆上固定长度数据的引用。
  • 一个 vector 开始有默认大小容量,当更多的元素被添加进来后,它会重新在堆上分配一个新的并具有更大容量的定长列表。(类似 C++ 的 vector)


fn main() {
    // 我们可以显式确定类型
    let mut i32_vec = Vec::<i32>::new(); // turbofish <3
    i32_vec.push(1);
    i32_vec.push(2);
    i32_vec.push(3);

    // 但是看看 Rust 是多么聪明的自动检测类型啊
    let mut float_vec = Vec::new();
    float_vec.push(1.3);
    float_vec.push(2.3);
    float_vec.push(3.4);

    // 这是个漂亮的宏!
    let string_vec = vec![String::from("Hello"), String::from("World")];

    for word in string_vec.iter() {
        println!("{}", word);
    }
}