简体   繁体   English

为什么 Rust 在使用“?”时会在错误类型之间隐式转换? 但不是返回值?

[英]Why does Rust implicitly convert between Error types when using '?' but not in return value?

Given the following two error types and functions to illustrate their usage ( Rust Playground link ):给出以下两种错误类型和函数来说明它们的用法( Rust Playground 链接):

#[derive(std::fmt::Debug)]
struct MyError;

#[derive(std::fmt::Debug)]
struct OtherError;

impl std::error::Error for MyError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

impl std::fmt::Display for MyError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "MyError")
    }
}

impl std::fmt::Display for OtherError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "OtherError")
    }
}

impl std::error::Error for OtherError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        None
    }
}

impl From<OtherError> for MyError {
    fn from(_: OtherError) -> Self {
        MyError {}
    }
}

fn my_error() -> Result<(), MyError> { Ok(()) }

fn other_error() -> Result<(), OtherError> { Ok(()) }

If I am in a function that returns Result with MyError as its Error type, I can call both functions returning MyError and OtherError because there's a From converting between them.如果我在返回ResultMyError中,它的Error类型为 MyError,我可以调用返回MyErrorOtherError的两个函数,因为它们之间有一个From转换。

However, I cannot simply return the Result for the "other" type, I need to use ?但是,我不能简单地返回“其他”类型的Result ,我需要使用? followed by Ok(()) instead.后跟Ok(())代替。 This looks inconsistent to me.这在我看来不一致。

For example, this works fine:例如,这很好用:

fn main() -> Result<(), MyError> {
    my_error()
}

This also does:这也可以:

fn main() -> Result<(), MyError> {
    my_error()?;
    other_error()?;
    my_error()
}

But this fails:但这失败了:

fn main() -> Result<(), MyError> {
    my_error()?;
    other_error()
}

Error:错误:

error[E0308]: mismatched types
  --> src/main.rs:43:5
   |
41 | fn main() -> Result<(), MyError> {
   |              ------------------- expected `std::result::Result<(), MyError>` because of return type
42 |     my_error()?;
43 |     other_error()
   |     ^^^^^^^^^^^^^ expected struct `MyError`, found struct `OtherError`
   |
   = note: expected enum `std::result::Result<_, MyError>`
              found enum `std::result::Result<_, OtherError>`

Why is that?这是为什么?

This makes some of my code more verbose, as I found out I need to do this to get it to work:这使得我的一些代码更加冗长,因为我发现我需要这样做才能让它工作:

fn main() -> Result<(), MyError> {
    my_error()?;
    other_error()?;
    Ok(())
}

Is this the only solution?这是唯一的解决方案吗? I am more interested in understanding the reason it works this way, but if I am doing something silly feel free to point out what could be done better.我对理解它以这种方式工作的原因更感兴趣,但如果我正在做一些愚蠢的事情,请随时指出可以做得更好的地方。

The ? ? operator is equivalent to the try!运算符相当于try! macro, which a simplified version is as follows:宏,其简化版如下:

macro_rules! r#try {
    ($expr:expr $(,)?) => {
        match $expr {
            Ok(val) => val,
            Err(err) => {
                return Err(From::from(err));
            }
        }
    };
}

You can find a reference for this in the book on the Recoverable Errors with Result page, in the A Shortcut for Propagating Errors: the?您可以在“ Recoverable Errors with Result ”一书中的A Shortcut for Propagating Errors: the? Operator section. 操作员部分。

There is a difference between what the match expression from Listing 9-6 does and what the ?示例 9-6 中的match表达式的作用与? operator does: error values that have the ?操作符:错误值有? operator called on them go through the from function, defined in the From trait in the standard library, which is used to convert errors from one type into another.运算符通过from function 调用它们 go,在标准库的From特征中定义,用于将错误从一种类型转换为另一种类型。 When the ?什么时候? operator calls the from function, the error type received is converted into the error type defined in the return type of the current function.操作符from function 调用,将接收到的错误类型转换为当前 function 的返回类型中定义的错误类型。 This is useful when a function returns one error type to represent all the ways a function might fail, even if parts might fail for many different reasons.当 function 返回一种错误类型来表示 function 可能发生故障的所有方式时,这很有用,即使部件可能由于许多不同的原因而发生故障。 As long as each error type implements the from function to define how to convert itself to the returned error type, the ?只要每个错误类型都实现from function 来定义如何将自己转换为返回的错误类型, ? operator takes care of the conversion automatically.操作员会自动处理转换。

A Shortcut for Propagating Errors: the? 传播错误的捷径:? Operator - The Rust Programming Language 运算符 - Rust 编程语言

(emphasis mine) (强调我的)

You can also find a reference for this in RFC 243 where you can find the information in the Exception type upcasting section.您还可以在RFC 243中找到对此的参考,您可以在Exception type upcasting部分中找到相关信息。

The ? ? operator should therefore perform such an implicit conversion, in the nature of a subtype-to-supertype coercion.因此,运算符应该执行这种隐式转换,本质上是子类型到超类型的强制转换。 The present RFC uses the std::convert::Into trait for this purpose (which has a blanket impl forwarding from From ).目前的 RFC 为此目的使用了std::convert::Into特征(它有一个从From转发的一揽子impl )。 [...] [...]

Exception type upcasting - RFC 243 异常类型向上转换- RFC 243

See also:也可以看看:

The Other uses of ?其他用途? page in Rust By Example also mentions the implicit conversion behavior. Rust By Example中的页面还提到了隐式转换行为。 It also provides an example of creating custom error types.它还提供了创建自定义错误类型的示例。


So in conclusion, the following:因此,总结如下:

fn main() -> Result<(), MyError> {
    my_error()?;
    other_error()?;
    Ok(())
}

Is essentially the equivalent to:本质上等同于:

fn main() -> Result<(), MyError> {
    match my_error() {
        Ok(_) => {}
        Err(err) => return Err(From::from(err));
    }
    match other_error() {
        Ok(_) => {}
        Err(err) => return Err(From::from(err));
    }
    Ok(())
}

In the case of returning other_error() and having it implicitly convert errors, then that would be kinda scary.在返回other_error()并让它隐式转换错误的情况下,那会有点可怕。 As if eg the return type was modified, and return... implicitly just Into , then it could introduce issues that aren't immediately obvious.好像例如返回类型被修改了,并且return...隐含地只是Into ,那么它可能会引入一些不是立即显而易见的问题。


If you want to avoid the additional Ok(()) , then you can use map_err() , ie .map_err(From::from) .如果你想避免额外的Ok(()) ,那么你可以使用map_err() ,即.map_err(From::from) However, if it isn't returned immediately then you can easily run into cases where the compiler cannot infer the correct type.但是,如果它没有立即返回,那么您很容易遇到编译器无法推断出正确类型的情况。

In those cases you can use a more explicit form, ie .map_err::<MyError, _>(From::from) , or just .map_err(MyError::from) .在这些情况下,您可以使用更明确的形式,即.map_err::<MyError, _>(From::from)或只是.map_err(MyError::from)

fn main() -> Result<(), MyError> {
    my_error()?;
    // other_error().map_err(From::from)
    // other_error().map_err::<MyError, _>(From::from)?;
    other_error().map_err(MyError::from)
}

If your MyError was an enum , which had a MyError::OtherError variant, then you could even just do .map_err(MyError::OtherError) .如果你的MyError是一个enum ,它有一个MyError::OtherError变体,那么你甚至可以只做.map_err(MyError::OtherError) This is because, not only does constructing an enum look like a function call, it is actually implemented as functions .这是因为,构造enum不仅看起来像 function 调用,它实际上是作为函数实现的

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM