Rust学习(八):异常处理和宏编程:

Rust学习(八):异常处理和宏系统

1、异常处理:

异常处理是任何编程语言都会遇到的现象,Rust并没有像其他变成语言一样提供了try catch这样的异常处理方法,而是提供了一种独特的异常处理机制。这里需要指明的是作者在书中将Rust中的失败、错误、异常等统称为异常,Rust中的异常有4种:Option, Result, Panic和Abort。

Option用于应对可能的失败情况,Rust使用Some和None来表示是否失败,以获取值为例:如果没有获取到值,就会返回None作为返回值,这时不需要报错,只需要依据情况处理即可。失败和错误不同,程序执行失败意味着代码设计是符合逻辑的,不会导致程序出问题(比如:需要浮点数类型,实际上是字符类型),Option的定义如下:

rust 复制代码
enum Option<T> {
    Some(T),
    None,
}

Result用于应对可恢复的错误,和Option十分相似,Option可以看成是Result<T, ()>,下面是Result的定义:

rust 复制代码
enum Result<T, E> {
    Ok(T),
    Err(E),
}

打开不存在或者没有权限的文字,或者将非数字字符串转换成数字,都会得到Err(E):

rust 复制代码
use std::fs::File;
use std::io::ErrorKind;

let f = File::open("kw.txt");

let f = match f {
    Ok(file) => file,
    Err(err) => match err.kind()
    ErrorKind::NotFound => match File::create("kw.txt") {
            Ok(fc) => fc,
            Err(e) => panic("Error while creating file"),
        }
    ErrorKind::PermissionDenied => panic("No permission!"),
    other => panic("Error while openning file"),
}

这里对可能遇到的错误逐一进行了处理,实在无法处理才会使用Panic,这一点一定要紧记:测试时使用Panic,实际上线时,最好使用错误处理机制!Panic是在遇到不可回复的错误时,清理内存,如果使用操作系统来清理内存,可以使用Abort。

Rust提供了unwrap或expect,来简化错误处理的流程:

rust 复制代码
use::std::fd::File;
use::std::io;
use::std::io::Read;

fn main() {
    let f = File::open("kw.txt").unwrap();
    let f = File::open("kw.txt").expect("Open file failed!");
}

如果遇到错误只想上抛,不想处理,可以使用"?"。此时返回类型是Result,成功返回String,错误返回:io::err:

rust 复制代码
fn main() {
    let s = read_from_file();
    match s {
        Err(e) => println!("{e}");
        Ok(s) => println!("{s}");
    }
}

fn read_from_file() -> Result<String, io::Error> {
    let f = File::open("kw.txt")?; //报错时直接抛出
    let mut s = String::new();
    f.read_to_string(&mut s);
    
    Ok(s)
}

2、宏系统:

Rust 中并不存在内置库函数,一切都需要自己定义。但是 Rust 实现了一套高效的宏,包括声明宏、过程宏,利用宏能完成非常多的任务。C 语言中的宏是一种简单的替换机制,很难对数据做处理;Rust 中的宏则强大得多,得到了广泛应用。比如,使用 derive 宏可以为结构体添加新的功能,常用的 println!、vec!、panic!等也是宏。

Rust 中的宏有两大类:一类是使用 macro rules! 声明的声明宏;另一类是过程宏,过程宏又分为三小类------derive 宏、类属性宏和类函数宏。因为前面使用过声明宏和 derive 宏,所以下面只打算介绍声明宏和 derive 宏。其他的宏,请通过查阅相关资料来学习。

声明宏的格式:macro_name!()、macro_name![]、macro_name!{}。首先是宏名,然后是感叹号,最后是()、[]或 {}。这些括号都可用于声明宏,但不同用途的声明宏使用的括号是不同的,比如是 vec![] 而不是 vec!(),带"()"的更像是函数,这也是 println!() 使用"()"的原因。不同的括号只是为了满足意义和形式上的统一,实际使用时任何一种都可以。

rust 复制代码
macro_rules! marco_name {
    ($matcher) => {
        $code_body;
        return_value
    };
}

在上面的宏定义中, matcher 用于标记一些语法元素,比如空格、标识符、字面值关键字、符号、模式、正则表达式,注意前面的符号 用于捕获值。code_body 将利用 Smatcher 中的值来进行处理,最后返回 return_value ,当然也可能不返回。比如,要计算二叉树中父节点 p 的左、右子节点,可以使用如下宏,节点 p 的左、右子节点的下标应该是 2p 和 2 p + 1 2p + 1 2p+1 :

rust 复制代码
macro_rules! left_child {
    ($parent:ident) => {
        $parent << 1
    };
}

macro_rules! right_child {
    ($parent:ident) => {
        ($parent << 1) + 1 
    };
}

过程宏更像是函数或一种过程。过程宏接收代码作为输入,然后在这些代码上进行操作并产生另一些代码作为输出。derive过程宏又称派生宏,形式如下:

rust 复制代码
#[derive(Clone)]
struct Student;

derive 过程宏通过这种标记形式为 Studen 实现了 Clone 里定义的方法,这样 Student 就可以直接通过调用 clone() 方法来实现复制。"这种形式其实就是 impl Clone for Student 的简易写法。derive 过程宏里也可以同时放多个 trait,这样就可以实现各种功能了:

rust 复制代码
#[derive(Dedug, Clone, Copy)]
struct Student;

宏属于非常复杂的系统,一般在必要且能简化代码的情况下使用宏。

相关推荐
violet-lz10 分钟前
数据结构四大简单排序算法详解:直接插入排序、选择排序、基数排序和冒泡排序
数据结构·算法·排序算法
·白小白15 分钟前
力扣(LeetCode) ——118.杨辉三角(C++)
c++·算法·leetcode
我的xiaodoujiao24 分钟前
使用 Python 语言 从 0 到 1 搭建完整 Web UI自动化测试学习系列 19--测试框架Pytest基础 3--前后置操作应用
python·学习·测试工具·pytest
std787928 分钟前
Rust 与 Go – 比较以及每个如何满足您的需求
开发语言·golang·rust
lzj_pxxw30 分钟前
嵌入式开发技巧:舍弃标志位,用宏定义函数实现程序单次运行
笔记·stm32·单片机·嵌入式硬件·学习
CoovallyAIHub1 小时前
超越“识别”:下一代机器视觉如何破解具身智能落地难题?
深度学习·算法·计算机视觉
仰泳的熊猫1 小时前
LeetCode:207. 课程表
数据结构·c++·算法·leetcode
liu****1 小时前
19.map和set的封装
开发语言·数据结构·c++·算法
水冗水孚1 小时前
双指针算法在实际开发中的具体应用之代码Review文章字符串的片段分割
算法·leetcode
DuHz1 小时前
用于汽车雷达应用的步进频率PMCW波形——论文阅读
论文阅读·算法·汽车·信息与通信·信号处理·毫米波雷达