Как я могу реализовать From для конкретных типов ошибок и Box <Error>в Rust? - PullRequest
3 голосов
/ 12 марта 2019

Вот мой тестовый код:

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

struct Handler {
    error: String
}

#[derive(Debug)]
struct SpecificError;

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

impl Error for SpecificError {}

impl<E: Error> From<E> for Handler {
    fn from(e: E) -> Self {
        Handler { error: format!("{}", e) }
    }
}

fn fail1() -> Result<(), SpecificError> {
    Err(SpecificError)
}

fn fail2() -> Result<(), Box<Error>> {
    Err(Box::new(SpecificError))
}

fn handler() -> Result<(), Handler> {
    fail1()?;
    fail2()?;
    Ok(())
}

Вызов fail1() в порядке, но вызов fail2() не компилируется:

error[E0277]: the size for values of type `dyn std::error::Error` cannot be known at compilation time
  --> src/main.rs:35:5
   |
35 |     fail2()?;
   |     ^^^^^^^^ doesn't have a size known at compile-time
   |
   = help: the trait `std::marker::Sized` is not implemented for `dyn std::error::Error`
   = note: to learn more, visit <https://doc.rust-lang.org/book/second-edition/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
   = note: required because of the requirements on the impl of `std::error::Error` for `std::boxed::Box<dyn std::error::Error>`
   = note: required because of the requirements on the impl of `std::convert::From<std::boxed::Box<dyn std::error::Error>>` for `Handler`
   = note: required by `std::convert::From::from`

Я согласен скомпилятор, который dyn Error не имеет размера, известного во время компиляции, но я не понимаю, почему это актуально, поскольку тип, из которого я пытаюсь преобразовать, - это Box<dyn Error>, размер которого известен привремя компиляции.

1 Ответ

5 голосов
/ 12 марта 2019

TL; DR: Я почти уверен, что вы не можете в общих чертах.

Я не понимаю, почему это так, поскольку тип, из которого я пытаюсь преобразовать, - это Box<dyn Error>, размер которого известен во время компиляции.

Это не то место, на которое он жалуется. Снова посмотрите на сообщение об ошибке (слегка очищенное):

the trait `Sized` is not implemented for `dyn Error`
required because of the requirements on the impl of `Error` for `Box<dyn Error>`
required because of the requirements on the impl of `From<Box<dyn Error>>` for `Handler`
required by `From::from`

Вторая строка является важной. Вот более простое воспроизведение вашей проблемы:

use std::error::Error;

fn example<E: Error>() {}

fn main() {
    example::<Box<dyn Error>>();
}
error[E0277]: the size for values of type `dyn std::error::Error` cannot be known at compilation time
 --> src/main.rs:6:5
  |
6 |     example::<Box<dyn Error>>();
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
  |
  = help: the trait `std::marker::Sized` is not implemented for `dyn std::error::Error`
  = note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
  = note: required because of the requirements on the impl of `std::error::Error` for `std::boxed::Box<dyn std::error::Error>`
note: required by `example`
 --> src/main.rs:3:1
  |
3 | fn example<E: Error>() {}
  | ^^^^^^^^^^^^^^^^^^^^^^

Error только реализован для Box<T>, когда T равен Sized и сам реализует Error:

impl<T: Error> Error for Box<T> {
    // ...
}

Сказал иначе, Box<dyn Error> не реализует Error.

Можно подумать, что вы можете добавить вторую реализацию From для Box<Error>, но это запрещено:

upstream crates may add new impl of trait `std::error::Error` for type
`std::boxed::Box<(dyn std::error::Error + 'static)>` in future versions

Лучшая альтернатива, которую я могу предложить, это реализовать From для каждого конкретного конкретного типа, который вам нужно поддерживать:

impl From<SpecificError> for Handler {
    fn from(e: SpecificError) -> Self {
        Handler { error: format!("{}", e) }
    }
}

impl From<Box<dyn Error>> for Handler {
    fn from(e: Box<dyn Error>) -> Self {
        Handler { error: format!("{}", e) }
    }
}

Макрос может уменьшить шаблон здесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...