《Rust语言圣经》入门实战的前两节中,介绍了 minigrep
程序设计过程,包括命令行设计、参数接收、文件读取、模块化和错误处理等。其功能是从指定文件中查找字符串。
代码包括两部分:main.rs,lib.rs。下面对代码简单做了注释。
main.rs
// 引入标准库中的环境变量和进程处理模块
use std::env;
use std::process;
// 引入自定义的minigrep模块中的Config结构体
use minigrep::Config;
// 主函数
fn main() {
// 获取命令行参数并存储到Vec类型的args中
let args: Vec<String> = env::args().collect();
// 使用Config::build方法解析命令行参数,如果解析失败则打印错误信息并退出程序
let config = Config::build(&args).unwrap_or_else(|err|{
println!("Problem parsing arguments: {}", err);
process::exit(1)
});
// 打印搜索关键词和文件名
println!("Searching for {}", config.query);
println!(" in file {}", config.filename);
// 调用minigrep模块的run方法执行搜索操作,如果发生错误则打印错误信息并退出程序
if let Err(e) = minigrep::run(config){
println!("Application error: {}", e);
process::exit(1);
}
// 程序正常结束,退出状态码为0
process::exit(0);
}
lib 模块
// 导入标准库中的文件系统模块和错误处理模块
use std::fs;
use std::error::Error;
// 定义一个名为Config的结构体,包含两个公共字段:query和filename
pub struct Config {
pub query: String,
pub filename: String,
}
// 为Config结构体实现一个名为build的方法,接收一个字符串切片作为参数,返回Result类型
impl Config{
// build方法用于根据传入的参数构建Config实例
pub fn build(args: &[String]) -> Result<Config, &'static str> {
// 如果参数数量小于3,返回错误信息
if args.len() < 3 {
return Err("Not enough arguments");
}
// 获取查询字符串和文件名
let query = args[1].clone();
let filename = args[2].clone();
// 返回构建好的Config实例
Ok(Config{ query, filename })
}
}
// 定义一个名为run的函数,接收一个Config实例作为参数,返回Result类型
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
// 读取文件内容到字符串中,如果出错则抛出异常
let contents = fs::read_to_string(config.filename)
.expect("Something went wrong reading the file");
// 打印文件内容
println!("With text:{}", contents);
// 返回成功
Ok(())
}
main.rs模块
lib.rs模块
在以上代码中,错误处理主要通过Result类型和unwrap_or_else方法来实现。
Result类型:用于表示一个操作可能成功或失败的结果。它有两个变体:Ok表示操作成功,并包含成功时的值;Err表示操作失败,并包含失败的原因。
unwrap_or_else方法:该方法用于处理Result类型的结果。如果结果是Ok,则返回Ok中的值;如果结果是Err,则执行传入的闭包函数,并返回该函数的结果。在这个例子中,当Config::build方法返回Err时,会执行闭包函数,打印错误信息并退出程序。
例如,在main.rs模块中,以下代码段:
let config = Config::build(&args).unwrap_or_else(|err|{
println!("Problem parsing arguments: {}", err);
process::exit(1)
});
这里使用了unwrap_or_else方法来处理Config::build方法的返回结果。如果build方法返回Ok,那么config变量将被赋值为Ok中的Config实例;如果build方法返回Err,那么将执行闭包函数,打印错误信息并退出程序。
lib.rs模块中:
if let Err(e) = minigrep::run(config){
println!("Application error: {}", e);
process::exit(1);
}
这里使用了if let
语句来处理minigrep::run
方法的返回结果。如果run
方法返回Ok,那么程序将继续执行;如果run
方法返回Err,那么将执行闭包函数,打印错误信息并退出程序。
Result<(), Box
是一个泛型类型,用于表示一个操作可能成功或失败的结果。它有两个变体:Ok表示操作成功,并包含成功时的值;Err表示操作失败,并包含失败的原因。
在这个例子中,Result的类型参数是(),表示操作成功时没有返回值。错误类型是Box
,其中Box
表示一个堆分配的指针,而dyn Error
表示任何实现了Error trait
的类型。这意味着这个Result
可以容纳任何实现了Error trait
的错误类型。
这种类型的Result通常用于那些不需要返回具体值的操作,只需要表示操作是否成功。例如,在minigrep::run函数中,它返回Result<(), Box
,表示该函数执行搜索操作,如果成功则不返回任何值(Ok(()))
,如果失败则返回一个实现了Error trait的错误类型(Err(error))
。
Rust中用于表示错误的标准接口。它定义了一组方法,用于处理错误和获取关于错误的信息。Error trait 常用方法:
description
:返回一个字符串切片,描述了错误的细节。cause
:返回一个Option类型的值,包含导致当前错误的原始错误。如果当前错误没有原因,则返回None。source
:返回一个Option类型的值,包含导致当前错误的原始错误。与cause方法类似,但source方法提供了更多的上下文信息。chain
:返回一个Option类型的值,包含一个错误链表,描述了导致当前错误的多个原始错误。如果当前错误没有原因,则返回None。backtrace
:返回一个Option类型的值,包含当前错误的回溯信息。这对于调试和定位错误非常有用。to_string
:返回一个字符串,包含了关于当前错误的详细信息。这个字符串通常包含了description、cause、source、chain和backtrace等方法的信息。要实现Error trait,类型需要提供上述方法的具体实现。Rust的标准库中已经为许多内置类型实现了Error trait,例如std::io::Error、std::num::ParseIntError等。用户也可以为自己的类型实现Error trait,以便在错误处理中使用。