将错误消息返回给期望“Box<dyn Error>”的函数

the*_*e88 10 error-handling rust

我是 Rust 的新手,正在尝试传播要在调用函数中处理的错误。从官方 Rust 书中,我读到 Result 'Box< dyn Error>' 用于表示捕获任何类型的错误,但我读得还不够深入,无法理解它的实际工作原理。

我有一个函数叫:

fn foo() -> Result<String, Box<dyn Error>> {
    Command::new("an_executable")
        .args(&["-p", path])
        .output()?;
    if condition {
        return Err("Error...");
    }
    // Do stuff, return String 
}
Run Code Online (Sandbox Code Playgroud)

因此,有人可以解释如果该返回类型满足条件,我应该如何返回错误。我是否必须更改返回类型或只返回不同的内容。在这种情况下,RUST 标准是什么?

当前编译错误是 Err("Error...") 与返回类型不匹配

Joe*_*lay 23

让我们专注于您的问题的绝对最小再现:

use std::error::Error;

fn foo() -> Result<String, Box<dyn Error>> {
    Err("Error...")
}
Run Code Online (Sandbox Code Playgroud)

此代码返回的错误是:

error[E0308]: mismatched types
 --> src/lib.rs:4:9
  |
4 |     Err("Error...")
  |         ^^^^^^^^^^ expected struct `std::boxed::Box`, found reference
  |
  = note: expected type `std::boxed::Box<dyn std::error::Error>`
             found type `&'static str`
Run Code Online (Sandbox Code Playgroud)

这是说函数签名希望您返回一个Err包含 a Box<dyn Error>,但您实际上返回了一个Err包含 a &str。由于类型不对齐,编译器会抛出错误。

解决这个问题的最简单方法是使用Intotrait,它实现了&str和之间的转换Box<dyn Error>

use std::error::Error;

fn foo() -> Result<String, Box<dyn Error>> {
    Err("Error...".into())
    // `Err(Box::<dyn Error>::from("Error..."))` would also work, but is more ugly!
}
Run Code Online (Sandbox Code Playgroud)

这个怎么运作

您可能仍然想知道这个神奇的.into()调用在幕后到底做了什么。

首先,让我们看看会发生什么,如果我们只是Box&str

use std::error::Error;

fn foo() -> Result<String, Box<dyn Error>> {
    Err(Box::new("Error..."))
}
Run Code Online (Sandbox Code Playgroud)
error[E0277]: the trait bound `&str: std::error::Error` is not satisfied
 --> src/lib.rs:4:9
  |
4 |     Err(Box::new("Error..."))
  |         ^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `&str`
  |
  = note: required for the cast to the object type `dyn std::error::Error`
Run Code Online (Sandbox Code Playgroud)

同样,这不起作用,因为类型不对齐 - 它期望Box包含实现Error特征的东西,但是如果您查看 docs,您会注意到它&str不是实现它的类型之一。您需要将您的字符串包装在一个确实实现的类型中Error

use std::error::Error;
use std::fmt;

#[derive(Debug)]
struct StrError<'a>(&'a str);

// Error doesn't require you to implement any methods, but
// your type must also implement Debug and Display.
impl<'a> Error for StrError<'a> {}

impl<'a> fmt::Display for StrError<'a>{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        // Delegate to the Display impl for `&str`:
        self.0.fmt(f)
    }
}

fn foo() -> Result<String, Box<dyn Error>> {
    Err(Box::new(StrError("Error...")))
}
Run Code Online (Sandbox Code Playgroud)

这段代码可以编译,基本上就是impl Into<Box<dyn Error>> for &str引擎盖下的内容:)

  • 谢谢大佬,用代码解决了问题。我需要阅读更多关于 RUST 的内容才能完全理解内部工作原理,但这个修复按预期工作。非常感激! (2认同)