首页 > 解决方案 > 为什么普通匹配表达式可以编译,而 map_err 调用却不能?

问题描述

使用 rustc 1.30.1 和 reqwest 0.9.5。

我有一个调用其他几个函数的函数,这些函数可能会返回不同类型的错误,尤其是std::io::Errorreqwest::Error.

要将这些传播给调用者,最简单的解决方案似乎是将它们放在 a 中Box,这样可以方便地实现From<Error>特征以及Error特征本身。像这样:

fn fetch_input() -> Result<String, Box<dyn Error>> {
    ...
    let session_cookie = load_session_cookie()?; // Returns Result<String, io::Error>
    let text: Result<String, reqwest::Error> = ...;
    text.map_err(Box::new) // Compile error on this line
}

但是,该代码无法编译:

error[E0308]: mismatched types                                                               
  --> src/main.rs:26:5                                                                       
   |                                                                                         
16 | fn fetch_input() -> Result<String, Box<dyn Error>> {                 
   |                     ------------------------------ expected `std::result::Result<std::string::String, std::boxed::Box<(dyn std::error::Error + 'static)>>` because of return type
...                                                                                          
26 |     text.map_err(Box::new)                                                              
   |     ^^^^^^^^^^^^^^^^^^^^^^ expected trait std::error::Error, found struct `reqwest::Error`
   |                                                                                         
   = note: expected type `std::result::Result<_, std::boxed::Box<(dyn std::error::Error + 'static)>>`
              found type `std::result::Result<_, std::boxed::Box<reqwest::Error>>`           

如果我用map_err一个普通的旧match表达式替换调用,一切都很好:

    match text {
        Ok(t) => Ok(t),
        Err(e) => Err(Box::new(e)),
    }

map_err请注意,这与标准库中的实现主体相同。那么为什么我的map_err调用没有通过类型检查器呢?不用说,reqwest::Error确实实现了std::error::Errortrait

我也想知道'static错误消息中的生命周期是从哪里来的。如果它被证明是无关的,我可能会为它打开一个不同的问题。

标签: rust

解决方案


Box::new做一件事,而且只做一件事:接受 areqwest::Error并将其放入 aBox<reqwest::Error>中。

表达式 Box::new(e)正在做件事:调用Box::new将 areqwest::Error放入 aBox<reqwest::Error>中,然后强制将Box<reqwest::Error>a 放入 aBox<dyn Error>中。

强制类型是 Rust 通常试图避免的。 Box<T>Box<dyn Trait>(和其他类似的,直接指针→指针强制)是一个例外。特别是,Rust不会强制Result<T, Box<Err>>.Result<T, Box<dyn Error>>

至于你的旁白:因为dyn Trait总是需要一个相关的生命周期。当您放入dyn Trait一个盒子时,它被隐含地假定为'static。当你有&'a dyn Trait时,它被假定为'a


推荐阅读