Rust学习(通过例子学习Rust)(4)

Rust学习(通过例子学习Rust)(4)

十七 使用macro_rule 宏

Rust 提供了一个强大的宏系统,可进行元编程

17.1语法

模式与指示符
重载
重复

17.1.1 模式与指示符

宏的参数使用一个美元符号 $ 作为前缀,并使用一个指示符(designator)来注明类型:
全部的类型或提升符有
block
expr 用于表达式
ident 用于变量名或函数名
item
literal 用于字面常量
pat (模式 pattern)
path
stmt (语句 statement)
tt (标记树 token tree)
ty (类型 type)
vis (可见性描述符)

17.1.2 重载

宏可以重载,从而接受不同的参数组合 ;分割

17.1.3 重复

宏在参数列表中可以使用 + 来表示一个参数可能出现一次或多次,使用 * 来表示该参数可能出现零次或多次。

17.2 DRY (不写重复代码)
17.3 DSL(领域专用语言)

DSL 是 Rust 的宏中集成的微型 “语言”。这种语言是完全合法的,因为宏系统会把它转换成普通的 Rust 语法树,它只不过看起来像是另一种语言而已。这就允许你为一些特定功能创造一套简洁直观的语法(当然是有限制的)

17.4 接受多个参数

可变参数接口可以接受任意数目的参数。比如说 println 就可以,其参数的数目是由格式化字符串指定的。
此章其他两本书籍已经重点学习就不在重复了

十八 错误处理

在 Rust 中有多种处理错误的方式
显式的 panic 主要用于测试,以及处理不可恢复的错误
Option 类型是为了值是可选的、或者缺少值并不是错误的情况准备的
当错误有可能发生,且应当由调用者处理时,使用 Result

18.1 panic

panic!

18.2 Option 和 unwrap

在标准库(std)中有个叫做 Option(option 中文意思是 “选项”)的枚举类型,用于有 “不存在” 的可能性的情况。它表现为以下两个 “option”(选项)中的一个:

Some(T):找到一个属于 T 类型的元素
None:找不到相应元素

你可以使用 match 语句来解开 Option,但使用 ? 运算符通常会更容易
match 是处理 Option 的一个可用的方法,但你会发现大量使用它会很繁琐,特别是当操作只对一种输入是有效的时。这时,可以使用组合算子(combinator),以模块化的风格来管理控制流。

#![allow(dead_code)]

#[derive(Debug)] enum Food { Apple, Carrot, Potato }

#[derive(Debug)] struct Peeled(Food);
#[derive(Debug)] struct Chopped(Food);
#[derive(Debug)] struct Cooked(Food);

// 削皮。如果没有食物,就返回 `None`。否则返回削好皮的食物。
fn peel(food: Option<Food>) -> Option<Peeled> {
    match food {
        Some(food) => Some(Peeled(food)),
        None       => None,
    }
}

// 切食物。如果没有食物,就返回 `None`。否则返回切好的食物。
fn chop(peeled: Option<Peeled>) -> Option<Chopped> {
    match peeled {
        Some(Peeled(food)) => Some(Chopped(food)),
        None               => None,
    }
}

// 烹饪食物。这里,我们使用 `map()` 来替代 `match` 以处理各种情况。
fn cook(chopped: Option<Chopped>) -> Option<Cooked> {
    chopped.map(|Chopped(food)| Cooked(food))
}

// 这个函数会完成削皮切块烹饪一条龙。我们把 `map()` 串起来,以简化代码。
fn process(food: Option<Food>) -> Option<Cooked> {
    food.map(|f| Peeled(f))
        .map(|Peeled(f)| Chopped(f))
        .map(|Chopped(f)| Cooked(f))
}

// 在尝试吃食物之前确认食物是否存在是非常重要的!
fn eat(food: Option<Cooked>) {
    match food {
        Some(food) => println!("Mmm. I love {:?}", food),
        None       => println!("Oh no! It wasn't edible."),
    }
}

fn main() {
    let apple = Some(Food::Apple);
    let carrot = Some(Food::Carrot);
    let potato = None;

    let cooked_apple = cook(chop(peel(apple)));
    let cooked_carrot = cook(chop(peel(carrot)));

    // 现在让我们试试看起来更简单的 `process()`。
    let cooked_potato = process(potato);

    eat(cooked_apple);
    eat(cooked_carrot);
    eat(cooked_potato);
}

组合算子:and_then

#![allow(dead_code)]

#[derive(Debug)] enum Food { CordonBleu, Steak, Sushi }
#[derive(Debug)] enum Day { Monday, Tuesday, Wednesday }

// 我们没有制作寿司所需的原材料(ingredient)(有其他的原材料)。
fn have_ingredients(food: Food) -> Option<Food> {
    match food {
        Food::Sushi => None,
        _           => Some(food),
    }
}

// 我们拥有全部食物的食谱,除了法国蓝带猪排(Cordon Bleu)的。
fn have_recipe(food: Food) -> Option<Food> {
    match food {
        Food::CordonBleu => None,
        _                => Some(food),
    }
}


// 要做一份好菜,我们需要原材料和食谱。
// 我们可以借助一系列 `match` 来表达这个逻辑:
fn cookable_v1(food: Food) -> Option<Food> {
    match have_ingredients(food) {
        None       => None,
        Some(food) => match have_recipe(food) {
            None       => None,
            Some(food) => Some(food),
        },
    }
}

// 也可以使用 `and_then()` 把上面的逻辑改写得更紧凑:
fn cookable_v2(food: Food) -> Option<Food> {
    have_ingredients(food).and_then(have_recipe)
}

fn eat(food: Food, day: Day) {
    match cookable_v2(food) {
        Some(food) => println!("Yay! On {:?} we get to eat {:?}.", day, food),
        None       => println!("Oh no. We don't get to eat on {:?}?", day),
    }
}

fn main() {
    let (cordon_bleu, steak, sushi) = (Food::CordonBleu, Food::Steak, Food::Sushi);

    eat(cordon_bleu, Day::Monday);
    eat(steak, Day::Tuesday);
    eat(sushi, Day::Wednesday);
}

18.3 Result

Result 是 Option 类型的更丰富的版本,描述的是可能的错误而不是可能的不存在。
Ok:找到 T 元素
Err:找到 E 元素,E 即表示错误的类型。

Result 的map

use std::num::ParseIntError;

// 就像 `Option` 那样,我们可以使用 `map()` 之类的组合算子。
// 除去写法外,这个函数与上面那个完全一致,它的作用是:
// 如果值是合法的,计算其乘积,否则返回错误。
fn multiply(first_number_str: &str, second_number_str: &str) -> Result<i32, ParseIntError> {
    first_number_str.parse::<i32>().and_then(|first_number| {
        second_number_str.parse::<i32>().map(|second_number| first_number * second_number)
    })
}

fn print(result: Result<i32, ParseIntError>) {
    match result {
        Ok(n)  => println!("n is {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    // 这种情况下仍然会给出正确的答案。
    let twenty = multiply("10", "2");
    print(twenty);

    // 这种情况下就会提供一条更有用的错误信息。
    let tt = multiply("t", "2");
    print(tt);
}

给Result 起别名
当我们要重用某个 Result 类型时,该怎么办呢?回忆一下,Rust 允许我们创建别名。若某个 Result 有可能被重用,我们可以方便地给它取一个别名。
提前返回
在上一个例子中,我们显式地使用组合算子处理了错误。另一种处理错误的方式是使用 match 语句和提前返回(early return)的结合。
有时我们只是想 unwrap 且避免产生 panic。到现在为止,对 unwrap 的错误处理都在强迫我们一层层地嵌套,然而我们只是想把里面的变量拿出来。? 正是为这种情况准备的。? 之前是try! 宏

18.4 处理多种错误
18.5 遍历result 省略

十九. 标准库的学习

后续章节建议参考其他书籍学习,书本内容比较浅,参考意义不大;

Rust 的基础学习终于告一断落,后续主要是关于前后端框架和网络相关学习;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值