码迷,mamicode.com
首页 > 其他好文 > 详细

Rust中文翻译21

时间:2015-07-11 18:34:13      阅读:146      评论:0      收藏:0      [点我收藏+]

标签:rust   编程语言   

4.7 错误处理

有时候程序会发生错误.对于不可避免的事情发生时最好有一个计划来处理.Rust有丰富的处理错误的方法.


你的程序会出现两种类型的错误:失败和崩溃.我们先讨论两者的区别,然后讨论如何处理他们.然后,我们讨论把错误升级为崩溃.

Page 107

4.7.1 失败和崩溃

Rust使用两种错误的类型:失败和崩溃.失败是一种可以被挽回的错误.崩溃却不行.
"挽回"是什么意思呢?在多数情况下,很有可能会发生错误.例如,一个parse函数:

"5".parse();

这个方法将一个字符转换为另一个类型.但是因为他是字符,你不能保证这个转换可以正常工作.例如,它该被转换成什么类型呢?

"hello5world".parse();

这个就不能工作.所以我们知道这个函数只能在某些输入下才能工作.这是可以预期的行为.我们称这种错误是失败(failure).

另一方面,有时候,我们会遇到不可预期的错误,或者我们没法挽回的错误.一个经典的assert!的例子:

assert!(x == 5);

我们使用assert!来声明一个表达式是true.如果不是,那么就有错误发生.这个错误导致我们无法继续当前的状态.另一个例子是使用unreachable!()宏:

enum Event {
    NewRelease,
}
fn probability(_: &Event) -> f64 {
    // real implementation would be more complex, of course
    0.95
}
fn descriptive_probability(event: Event) -> &‘static str {
    match probability(&event) {
        1.00 => "certain",
        0.00 => "impossible",
        0.00 ... 0.25 => "very unlikely",
        0.25 ... 0.50 => "unlikely",
        0.50 ... 0.75 => "likely",
        0.75 ... 1.00 => "very likely",
    }
}

fn main() {
    std::io::println!(descriptive_probability(NewRelease));
}

我们得到了一个错误:
技术分享
虽然我们知道我们覆盖了所有的可能,但是Rust不能.他不知道可能区间是从0.0到1.0.所以我们需要加一个case:
use Event::NewRelease;

enum Event {
    NewRelease,
}
fn probability(_: &Event) -> f64 {
    // real implementation would be more complex, of course
    0.95
}
fn descriptive_probability(event: Event) -> &‘static str {
    match probability(&event) {
        1.00 => "certain",
        0.00 => "impossible",
        0.00 ... 0.25 => "very unlikely",
        0.25 ... 0.50 => "unlikely",
        0.50 ... 0.75 => "likely",
        0.75 ... 1.00 => "very likely",
        _ => unreachable!()
    }
}

fn main() {
    println!("{}", descriptive_probability(NewRelease));
}

我们不必一直增加这个_case,所以我们使用unreachable!()宏来指出这一点.它是另一种错误处理的方式.Rust称这种错误为panic.

4.7.2 使用Option和Result来处理错误

一个最简单的处理函数失败的方法就是使用Option<T>类型.例如,find方法在字符串中试图找到一个匹配的串,然后返回一个Option:

let s = "foo";

assert!(s.find(‘f‘), Some(0));
assert!(s.find(‘z‘), None);

这基本就是最简单的方法了,但是这样的话我们没法得知错误信息.如果我们想要知道为什么失败了呢?为此,我们需要使用Result<T,E>类型.像这样:

enum Result<T, E> {
    Ok(T),
    Err(E)
}

这个枚举有Rust语言自身提供了,所以你不必定义它.Ok(T)变量代表成功,Err(E)变量代表失败.返回一个Result类型而不是Option类型是推荐的方法,除非你不关心失败信息.

有一个列子:

#[derive(Debug)]
enum Version { Version1, Version2 }

#[derive(Debug)]
enum ParseError { InvalidHeaderLength, InvalidVersion }

fn parse_version(header: &[u8]) -> Result<Version, ParseError> {
    if header.len() < 1 {
        return Err(ParseError::InvalidHeaderLength);
    }
    match header[0] {
        1 => Ok(Version::Version1),
        2 => Ok(Version::Version2),
        _ => Err(ParseError::InvalidVersion)
    }
}
let version = parse_version(&[1, 2, 3, 4]);
match version {
    Ok(v) => {
        println!("working with version: {:?}", v);
    }
    Err(e) => {
        println!("error parsing header: {:?}", e);
    }
}

这个函数使用了一个枚举, ParseError, 来枚举不同的错误.Debug特性可以是我们通过{:?}格式操作符来打印枚举值.

4.7.3 panic!不可恢复错误

当非预期的或不可恢复的错误发生时,panic!宏触发一个崩溃.它会让当前线程崩溃然后给出一个错误:

panic!("boom");

输出

thread ‘<main>‘ panicked at ‘boom‘, hello.rs:2

因为这种情况很少发生,所以谨慎的使用它.

4.7.4 将失败升级为崩溃

在某些情况下,甚至当一个函数失败的时候,我们希望把它当作已给崩溃而不是失败来处理.例如,io::stdin().read_line(&mut buffer)返回了一个Result<usize>,当读取一行输入失败的时候发生.这是我们可以处理这个失败并且恢复它.

如果我们不想处理这个错误,我们就想要终止程序运行,我们可以试用unwrap()方法.

io::stdin().read_line(&mut buffer).unwrap();

当Result是一个Err时,unwrap()会panic!.这意味着"给我值,如果那里出错了,让程序崩溃."这比起尝试恢复程序来说有点不靠谱,但是却更简洁.有时候,崩溃就对了.

另一种方法比unwrap()要好一点儿:

let mut buffer = String::new();
let input = io::stdin().read_line(&mut buffer)
                       .ok()
                       .expect("Failed to read line");

ok()把Result转化为Option,expect()对unwrap()做了同样的事,但是包含一条信息.这条信息传给了潜在的panic!,它提供了一个更好的带有错误信息的错误.

Page 111

4.7.5 使用try!

当调用多个函数都返回Result类型的时候,错误处理写起来会很繁琐.使用try!宏可以隐藏那些在调用栈上弥漫开来的错误信息.
把下述代码:
use std::fs::File;
use std::io;
use std::io::prelude::*;
struct Info {
    name: String,
    age: i32,
    rating: i32,
}
fn write_info(info: &Info) -> io::Result<()> {
    let mut file = File::create("my_best_friends.txt").unwrap();
    if let Err(e) = writeln!(&mut file, "name: {}", info.name) {
        return Err(e)
    }
    if let Err(e) = writeln!(&mut file, "age: {}", info.age) {
        return Err(e)
    }
    if let Err(e) = writeln!(&mut file, "rating: {}", info.rating) {
    return Err(e)
}
    return Ok(());
}
替换为:
use std::fs::File;
use std::io;
use std::io::prelude::*;
struct Info {
    name: String,
    age: i32,
    rating: i32,
}
fn write_info(info: &Info) -> io::Result<()> {
    let mut file = try!(File::create("my_best_friends.txt"));
    try!(writeln!(&mut file, "name: {}", info.name));
    try!(writeln!(&mut file, "age: {}", info.age));
    try!(writeln!(&mut file, "rating: {}", info.rating));
    return Ok(());
}

将代码包裹在try!中会导致成功值,除非Result是Err,函数将会提前返回.

需要注意的是你只能在一个函数返回Result的时候使用try!,也就是说你不能在main()函数中使用try!,因为main()函数不返回任何值.

try!使用了From<Error>来决定返回什么错误.

Rust中文翻译21

标签:rust   编程语言   

原文地址:http://blog.csdn.net/zcmit/article/details/46842981

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!