Rust 错误处理的五种方式及学习特质如何支持继承

开发 前端
在Rust中,大约有20种处理错误的模式,而我们这里只看到了其中的5种。练习多种错误模式将有助于更容易地识别这些模式。

在编写代码时,学习如何处理错误是非常重要的。错误通常是由于用户对程序目的的误解而产生的。错误不仅可以作为用户的教学工具,还能提高代码的效率。与我们用于控制流程的逻辑条件不同,错误是由于某种期望未被满足或某个选项不可用而产生的。Rust提供了五种不同的方法来直接解决这些“期望”和“不可用”的问题。

错误最终服务于用户和开发者

当用户收到晦涩难懂的错误反馈时,他们会感到沮丧。而开发者则希望看到导致崩溃的一系列事件或函数调用。这导致我们将错误视为字符串和结构化类型。

观察Python如何处理错误

在学习Rust的错误处理时,通过比较和对比Python的错误处理方式是非常有效的。这样可以更容易地发现模式。

Python与Rust的比较与对比

无错误处理:主函数引发异常并失败
result = 10 / 0
print(result)

输出:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
Try Except:捕获边界情况
try:
    result = 10 / 0  # This will raise ZeroDivisionError
except ZeroDivisionError:
    print("Cannot divide by zero!")
except TypeError:
    print("Type error occurred!")
Try Except Finally:确保异常期间的正确清理
try:
    file = open("example.txt", "r")
except FileNotFoundError:
    print("File not found.")
finally:
    print("Closing file (if open).")
    if 'file' in locals() and not file.closed:
        file.close()
Raising Exception:崩溃代码/由调用函数处理
age = -1
if age < 0:
    raise ValueError("Age cannot be negative!")
创建自定义错误:为开发者提供结构化错误
class ApplicationError(Exception):
    """Base class for all application errors."""
    pass

class DatabaseError(ApplicationError):
    """Exception raised for database-related errors."""
    pass

class APIError(ApplicationError):
    """Exception raised for API-related errors."""
    pass

# Usage
try:
    raise DatabaseError("Unable to connect to the database.")
except DatabaseError as e:
    print(f"Database error: {e}")
except ApplicationError:
    print("General application error.")

通过这些处理,程序可以继续运行,直到用户关闭应用程序。在此过程中,我们可以看到用户请求是如何被服务的,以及支持设备和应用程序在做什么。

观察Rust如何处理错误

现在轮到Rust了。错误处理中的术语如Panic、Unwrap、Expect,以及“?”,每一个都让我们采取行动,编写更好的代码。在Rust中让代码Panic被认为是不好的做法。构建应用程序或库时,定义良好的错误处理是关键。

无错误处理:Panic!

fn main() {
    let x = 50;
    let y = 0;
    let rs = x / y;
    println!("{}", rs)
}

输出:

thread 'main' panicked at 'attempt to divide by zero', src/main.rs:11:14

Catch Unwind:捕获边界情况

fn main() {
    let x = 50;
    let y = 10;
    let cuw = std::panic::catch_unwind(|| x / y);
    match cuw {
        Ok(val) => println!("Got the {val}"),
        Err(e) => println!("Error: {:?}", e),
    }
}

Rust中的存在检测:match、Results和Options

Rust是一种强类型和内存安全的语言,这导致了Enum的独特子数据类型Options和Results。两者都处理我们感兴趣的事物的“存在”和“不存在”。match关键字用于检查返回类型是否为Enum的任何变体。在Option的情况下,它是None或一个可以处理和使用的“已知Rust数据类型”。在Result的情况下,它是Error或我们追求的“值”。

在函数中引发错误:传播错误

use std::error::Error;

fn div_by_zero(a: i32, b: i32) -> Result<i32, Box<dyn Error>> {
    if b == 0 {
        return Err("Divide by 0, leads to infinity....".into());
    }
    Ok(a / b)
}

fn main() {
    println!("Doing what catch unwind does, manually");
    let err_rs = div_by_zero(57, 0);
    let val_rs = div_by_zero(50, 5);
    match err_rs {
        Ok(val) => println!("Got {val}"),
        Err(e) => eprintln!("Got {:?}", e),
    }
    match val_rs {
        Ok(val) => println!("Got {val}"),
        Err(e) => eprintln!("Got {:?}", e),
    }
}

创建自定义错误:为开发者提供结构化错误输出

use std::error::Error;
#[derive(Debug)]
struct ZeroDivideError {
    details: String,
}

impl ZeroDivideError {
    fn new(msg: &str) -> ZeroDivideError {
        ZeroDivideError {
            details: msg.to_string(),
        }
    }
}

use std::fmt::Display;
impl Error for ZeroDivideError {}
impl Display for ZeroDivideError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Zero divide: {}", self.details)
    }
}

fn div_by_zero_error(a: i32, b: i32) -> Result<i32, ZeroDivideError> {
    if b == 0 {
        return Err(ZeroDivideError::new("Structured output"));
    }
    Ok(a / b)
}

fn main() {
    println!("Creating Custom Errors");
    let err_rs = div_by_zero_error(57, 0);
    let val_rs = div_by_zero_error(50, 5);
    match err_rs {
        Ok(val) => println!("Got {val}"),
        Err(e) => eprintln!("Got {:?}", e),
    }
    match val_rs {
        Ok(val) => println!("Got {val}"),
        Err(e) => eprintln!("Got {:?}", e),
    }
}

输出:

Creating Custom Errors
Got ZeroDivideError { details: "Structured output" }
Got 10

错误类型依赖于实现的Traits

结构体ZeroDivideError是Rust中的一个标准结构体。在Rust中,结构体相当于Python中的类。通过impl Error for ZeroDivideError,我们将ZeroDivideError变成了一个“错误类型结构体”。我们还需要为ZeroDivideError实现std::fmt::Display,以便将错误显示给用户或开发者。

动态处理不同错误:使用Box

Box<dyn Error>在函数可能返回不同类型的错误时非常有用,可以统一处理这些错误。动态分派(dyn)通过允许运行时多态性实现这一点。

use std::error::Error;
#[derive(Debug)]
struct ZeroDivideError {
    details: String,
}

impl ZeroDivideError {
    fn new(msg: &str) -> ZeroDivideError {
        ZeroDivideError {
            details: msg.to_string(),
        }
    }
}

use std::fmt::Display;
impl Error for ZeroDivideError {}
impl Display for ZeroDivideError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "Zero divide: {}", self.details)
    }
}

// Combining different error types using `Box<dyn Error>`
pub fn parse_and_double(input: &str) -> Result<i32, Box<dyn Error>> {
    let number = input.parse::<i32>()?;
    Ok(number * 2)
}

pub fn parse_and_dbl(input: &str) -> Result<i32, ZeroDivideError> {
    let number = input.parse::<i32>();
    match number {
        Err(_) => {
            return Err(ZeroDivideError::new(
                "Negative number due to number parsing",
            ))
        }
        Ok(number) => return Ok(number * 2),
    }
}

fn main() {
    println!("Creating Custom Errors");
    let parseme = parse_and_double("56,");
    let prsme = parse_and_dbl("56,");
    match parseme {
        Ok(val) => println!("Got {val}"),
        Err(e) => eprintln!("Got {:?}", e.to_string()),
    }
    match prsme {
        Ok(val) => println!("Got {val}"),
        Err(e) => eprintln!("Got {:?}", e.to_string()),
    }
}

输出:

Creating Custom Errors
Got "invalid digit found in string"
Got "Zero divide: Negative number due to number parsing"

这并不是Rust中错误处理的全部。在Rust中,大约有20种处理错误的模式,而我们这里只看到了其中的5种。练习多种错误模式将有助于更容易地识别这些模式。

责任编辑:武晓燕 来源: Rust开发笔记
相关推荐

2024-03-05 18:15:28

AsyncAwait前端

2023-10-08 20:31:18

React

2023-03-10 08:48:29

2023-02-22 16:33:04

前端JavaScript

2015-08-19 14:11:56

SQL Server错误处理

2023-10-26 12:05:14

Golang开发

2021-04-14 07:08:14

Nodejs错误处理

2024-03-27 08:18:02

Spring映射HTML

2014-01-13 10:36:53

C++错误

2021-04-29 09:02:44

语言Go 处理

2014-11-17 10:05:12

Go语言

2022-02-23 12:35:12

LibreOffic无障碍辅助套件

2023-12-26 22:05:53

并发代码goroutines

2022-12-30 11:05:40

Rust代码

2023-10-28 16:30:19

Golang开发

2009-08-05 16:04:50

2010-06-01 16:14:04

2022-11-16 08:41:43

2013-01-09 15:46:02

Android百度定位SDKGPS

2023-12-19 23:00:21

点赞
收藏

51CTO技术栈公众号