Catalyst

Rust 笔记(二)其它类型

匹配、向量组、~~哈希表~~和错误处理。

先过一遍基础,之后写东西遇到问题再单拎出来写整合博文……这么想的。

2022-01-16:哈希表移到了 Collection 合集里。

匹配和枚举

Option 和 Result

enum Fruit {
    Apple,
    Banana,
    Pear,
    Orange,
}

fn show_fruit(fruit: Fruit) -> String {
    match fruit {
        Fruit::Apple => "apple".to_string(),
        Fruit::Banana => "banana".to_string(),
        Fruit::Pear => "pear".to_string(),
        Fruit::Orange => "orange".to_string(),
    }
}

fn main() {
    let myfavorite = Fruit::Orange;
    println!("my favorite fruit is {}", show_fruit(myfavorite));
}

对于匹配时没有匹配上的项目留下的默认选项:_ => <exp>,和 Elm 一样。

利用类型选项来进行匹配(类似 Elm 的Maybe)。

fn main() {
    print_number(Some(32));
    print_number(None);
}

fn print_number(str: Option<i32>) {
    match str {
        Some(s) => println!("你输入的数字是:{}", s),
        None => println!("输入为空!")
    }
}

可以对枚举成员添加属性描述:

enum Message{
	Id(u32),
	Position {x:i32,y:i32},
	Msg(String),
	Color(i32,i32,i32)
}

match message {
	Message::Id(id) => ...
}

if let 和 while let

有时需要这样的使用场景

if let Some(i) = value {
	...
}

while let Some(i) = value{
	...
}

while letif letvalue解构到 Some(i) 中。如果valueNone则为否/跳过。

向量 | Vectors

fn main() {
    //向量组
    let mut v: Vec<i32> = Vec::new();
    //向向量组内添加
    v.push(1);

    //根据内容创建向量组
    let v2 = vec![1, 2, 3];

    //读取数据,
    let data: &i32 = &v2[0];
    let data2: std::option::Option<&i32> = v2.get(0); //可能 get 到的是 None
    //也可以通过 v2[0] 直接取值。
    //但如果越界则会直接 panic,使用 get 则可以处理“如果越界”的情况。
}

迭代:

    for i in &v2{
        println!("{}", i);
    }

    for i in &mut v {
        *i +=1; //更改可变引用所引用的值,使用解除引用符号(*)
        println!("{}", i);
    }

复制

由于 Rust 默认不可变,所以如下的代码片段编译不会通过:

fn main(){
    let v = vec![1,2,3];
    v[0] = 3;
    println!("{:?}",v)
}

考虑使用to_vec复制内容:

fn main() {
    let v = vec![1, 2, 3];
    let v2 = &mut v.to_vec();
    v2[0] = 3;
    println!("{:?}", v);
    println!("{:?}", v2);
}

编译通过,运行结果:

[1, 2, 3]
[3, 2, 3]

to_vec 的定义

pub fn to_vec(&self) -> Vec<T, Global> where
    T: Clone, 

Copies self into a new Vec.

复制切片 (slice):

let mut x = vec![0; 3]; //[0,0,0]
let y = vec![1, 2, 3, 4, 5, 6, 7];
x.clone_from_slice(&y[0..3]);
println!("{:?}", x); // [1, 2, 3]

错误处理

Rust有一个panic!宏。碰到无法处理的错误时,该宏运行,程序将输出错误信息并清理堆栈,退出程序。

//对于可能出现的错误,使用 Result 类型处理。
//enum Result<T, E> {
//     Ok(T),
//     Err(E),
// }

fn main() {
    let input1 = "abcde";

    let _number: i32 = match input1.parse() {
        Ok(num) => num,
        Err(_) => panic!("该字符串不是数字!"),
    };
    //thread 'main' panicked at '该字符串不是数字!', ./error.rs:13:19
}

错误的再匹配:error.kind查看错误类型。

简写:

//如果失败会触发 panic(由 unwrap 触发)
let _n1: i32 = String::from("abced").parse().unwrap();
//thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }', ./error.rs:11:50

使用expect可以提供自定义错误信息。

let _n2: i32 = String::from("abced").parse().expect("non-number");
//thread 'main' panicked at 'non-number: ParseIntError { kind: InvalidDigit }', ./error.rs:11:50

?接在Result型后面,如果 Ok 则返回值,不然返回并传播 Err(退出整个函数)。所以整个函数的返回值是Result,例如Ok(())

use std::num::ParseIntError;
fn pars() -> Result<i32, ParseIntError> {
    let n3 = "abcde";
    let n4 = n3.parse::<i32>()?;
    Ok(n4)
}
//调用 pars() 并打印结果
//Err(ParseIntError { kind: InvalidDigit })

Box<dyn std::error::Error>可以接受任何类型的error。在函数中,可以把多种类型的错误展成此一种错误。dyntrait对象的前缀,说明对trait上方法是动态调用的。

虽然使用 ? 返回的应该是同一种错误。但可以让类型定义Trait std::convert::From<>来自动转换。

迭代器 | Iterator

迭代器 (Iterator)。可实现对序列化对象的遍历。详细可用方法

所有Iterator均实现一个next方法以索引到下一个对象(Some或者None)。

迭代器的消耗:可以理解为迭代器内部维护着目前的迭代情况。需要注意的是,迭代器如果不消耗,设定的方法也不会运行。

  • collect将迭代器转换回集合。有时需要注明类型,例 a.iter().collect::<String>()
  • last 运行(消耗)迭代器,返回最后一个元素。

按字符迭代字符串:

let chars: Vec<char> = str.chars().collect();
参考资料