大家好,我是梦兽编程。欢迎回来与梦兽编程一起刷Rust的系列。
这是由 Google 的 Android开发团队的分享Rust课程。本课程涵盖了 Rust 的方方面面,从基本语法到泛型和错误处理等高级主题。
该课程的最新版本可以在 google.github.io/comprehensi...
如果你喜欢看梦兽编程的版本可以订阅跟着谷歌安卓团队学Rust订阅最新内容,梦兽编程也期待大家关注我的个人网站。
加入梦兽编程微信群,微信搜索【梦兽编程】公众号 回复 111 即可加入交流群与梦兽进行交流。
异常
在日常开发过程,我们会经常遇到运行过程中出现一些奇奇怪怪的错误。有些错误可能会导致程序无法正常执行或者结束。
这种直接让程序崩溃,我们应该尽可能的减少。Rust在运行时发生致命的错误会触发panic来应对。
ini
fn main() {
let v = vec![10, 20, 30];
println!("v[100]: {}", v[100]);
}
触发panic的情况有三种
- 失败的边界检查(如数组越界访问)
- 断言失败(例如使用assert!宏)
- 使用panic!宏让程序终止。客户端没权限的软件通常的做法
上面的例子就是第一种情况。
我们要如何处理异常呢?
很多时候我们出现异常,是不想程序因为这些错误而直接导致程序无法运行。比如我们想在想打开一个文件,这个文件不存在时会触发panic!,如果你正在开发一款Photoshop的软件,这种迷之操作会直接让用户崩溃。所以我们需要对这些异常做可控的处理。
异常处理的模板,现代语言中golang的异常处理非常简单。
go
package main
import (
"io/ioutil"
"log"
)
func main() {
path := "/tmp/dat" //文件路径
file, err := readFile(path)
if err != nil {
log.Fatal(err) //错误打印
}
println("%s", file) //打印文件内容
}
func readFile(path string) (string, error) {
dat, err := ioutil.ReadFile(path) //读取文件内容
if err != nil { //判断err是否为nil
return "", err //不为nil,返回err结果
}
return string(dat), nil //err=nil,返回读取文件内容
}
在Golang是用if err ≠ nil 这种方式进行处理,Rust提供类似的方式通过match的方式进行异常处理。
「注意:网上很多人会使用unwrap,它主要用于Option
或Result
的打开其包装的结果。可能因为没有程序检查或校验,潜在的bug可能就出现其中,使得我们程序往往就panic了。这可能使我们最不愿看到的现象。你非常自信的情况下可以unwrap,快速处理简洁的处理是unwrap的特点。」
rust
use std::io::Read;
use std::{fs, io};
// 如果你想使用 ? 操作符号简化你的异常处理,需要你在函数执行的地方返回一个Result才能使用
fn read_username(path: &str) -> Result<String, io::Error> {
let username_file_result = fs::File::open(path);
let mut username_file = match username_file_result {
Ok(file) => file,
Err(err) => return Err(err),
};
let mut username = String::new();
match username_file.read_to_string(&mut username) {
Ok(_) => Ok(username),
Err(err) => Err(err),
}
}
fn main() {
//fs::write("config.dat", "alice").unwrap();
let username = read_username("config.dat");
println!("username or error: {username:?}");
}
如果你想使用?操作符。需要在函数中返回Ruslt才可以使用。我们处理隐藏的经典模板如下:
scss
match expression {
Ok(value) => value,
Err(err) => return Err(From::from(err)),
}
这个时候我们就可以使用?操作符来简化我们的操作。
rust
use std::error::Error;
use std::fs;
use std::io::Read;
fn read_count(path: &str) -> Result<i32, Box<dyn Error>> {
let mut count_str = String::new();
fs::File::open(path)?.read_to_string(&mut count_str)?;
let count: i32 = count_str.parse()?;
Ok(count)
}
fn main() {
fs::write("count.dat", "1i3").unwrap();
match read_count("count.dat") {
Ok(count) => println!("Count: {count}"),
Err(err) => println!("Error: {err}"),
}
}
std::error::Error 是 Rust提供的一个Trait接口,详细的内容可以查阅doc.rust-lang.org/std/error/t...。
rust
///自定义类型 Error,实现std::fmt::Debug的trait
#[derive(Debug)]
struct CustomError {
err: ChildError,
}
///实现Error的trait,因为有子Error:ChildError,需要覆盖source()方法,返回Some(err)
impl std::error::Error for CustomError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.err)
}
}
使用社区方案处理异常
首先,你需要在Cargo.toml文件中添加thiserror和anyhow作为依赖项:
ini
[dependencies]
thiserror = "1.0"
anyhow = "1.0"
然后,你可以使用thiserror来定义一个自定义错误类型:
rust
use thiserror::Error;
#[derive(Error, Debug)]
enum CustomError {
#[error("无法打开文件")]
FileOpenError(#[from] std::io::Error),
#[error("无法解析JSON")]
JsonParseError(#[from] serde_json::Error),
}
我们定义了一个CustomError枚举,它包含了两种可能的错误:文件打开错误和JSON解析错误。使用#[error]属性,我们可以为每个错误变体提供一个描述性消息。#[from]属性则用于从其他错误类型(如std::io::Error和serde_json::Error)自动转换。
接下来,我们可以使用anyhow来处理这些错误:
rust
use anyhow::Context;
use std::fs::File;
use std::io::Read;
use serde_json::Value;
fn read_json_file(file_path: &str) -> Result<Value, anyhow::Error> {
let mut file = File::open(file_path).context("无法打开文件")?;
let mut content = String::new();
file.read_to_string(&mut content).context("无法读取文件内容")?;
let json_value = serde_json::from_str(&content).context("无法解析JSON")?;
Ok(json_value)
}
fn main() {
match read_json_file("example.json") {
Ok(value) => {
println!("JSON解析成功: {:?}", value);
}
Err(e) => {
eprintln!("发生错误: {}", e);
}
}
}
在这个例子中,read_json_file函数尝试打开并读取一个JSON文件。如果任何一步失败,它将使用anyhow::Context来添加上下文信息,并返回一个anyhow::Error。在main函数中,我们调用read_json_file并处理可能发生的错误。 这样,我们就可以使用thiserror来定义自定义错误类型,并使用anyhow来方便地处理和传递这些错误。 希望这篇关于Rust异常错误处理的分享对您有所帮助。Rust以其强大的内存安全特性和高效性能而备受关注,而熟练掌握其异常处理机制对于编写健壮的Rust程序至关重要。如果您希望继续深入学习Rust,或者对编程有任何疑问,欢迎关注我的公众号"梦兽编程",我将持续分享编程知识,并与您一同交流。再次感谢您的阅读,期待与您在"梦兽编程"公众号相见!
本文使用 markdown.com.cn 排版