LINUX.ORG.RU

Тип-обёртка для ошибок в Rust

 


0

2

Допустим, я хочу сделать тип MyError, в который бы я мог упихать произвольную ошибку - хочу возвращать свой тип ошибки в Result методов моего трейта, чтобы абстрагироваться от деталей реализации. При этом я хочу, чтобы при написании реализации этих методов я мог использовать оператор вопроса с автоматическим преобразованием типа ошибки, а не вызывать вручную map_err.

use derive_more::Display;
use thiserror::Error;

#[derive(Error, Debug, Display)]
pub struct MyError(#[source] Box<dyn std::error::Error>);

impl<T: std::error::Error> From<T> for MyError {
	fn from(value: T) -> Self {
		Self(Box::new(value))
	}
}

Получаю ошибку компиляции:

error[E0119]: conflicting implementations of trait `From<MyError>` for type `MyError`
 --> src\mod.rs:9:1
  |
9 | impl<T: std::error::Error> From<T> for MyError {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: conflicting implementation in crate `core`:
          - impl<T> From<T> for T;

Как я понимаю, проблема в том, что MyError сам по себе реализует трейт Error и получается что-то вроде рекурсии.

Что с этим делать?

★★★★★

Это известная проблема и причина, почему обобщённые ошибки в anyhow, eyre, failure не реализуют std::error::Error.

Ситуация в будущем несколько улучшится со стабилизацией специализации, но сейчас красивого решения нет.

Насколько я понимаю, у тебя ошибка твоей библиотеки (иначе зачем ещё thiserror, а не обобщённая ошибка). Самое простое здесь это конкретные From инстансы:

impl From<hyper::Error> for MyError {
    fn from(error: hyper::Error) -> Self {
        Self::Network(Box::new(error)) // Box<dyn ...>
    }
}

Минус этого решения в том, что эти инсансы являются частью публичного интерфейса и поэтому раскрывают реализацию. Поэтому более чистое решение это всё-таки приватные MyError::network(..) конструкторы. Да, ? не заиспользовать сразу, придётся делать res.map_err(MyError::network)?, но зато реализация скрытой получается.


Если же у тебя приложение и тебе деление на варианты (Network/Codec/etc) не нужны, то просто используй готовые eyre/anyhow. Они не реализуют std::error::Error, но это и неважно для небиблиотечного кода.

loyd
()
Последнее исправление: loyd (всего исправлений: 1)
Ответ на: комментарий от hbee

Он покрывает все потребности в обработке ошибок и является убийцей anyhow, thiserror и прочих более ранних библиотек

Мне кажется ты троллишь. anyhow, eyre, failure это одна история, snafu, thiserror, derive-more это другая история и в одном предложении они стоять не могут.

loyd
()
Ответ на: комментарий от loyd

Очень даже могут. Все крейты, стирающие типы (anyhow и иже с ними) должны отправляться в топку после появления S.N.A.F.U. Эфемерные «удобства», предоставляемые ими, выливаются в головняк при сопровождении. И неважно, «библиотека» разрабатывается или «приложение».

hbee ★★★★
()
Ответ на: комментарий от loyd

Ситуация в будущем несколько улучшится со стабилизацией специализации

Блин, да когда уже? :-)

Я со времён Rust 1.0 жду. Уже const generics завезли, а специализация и ныне там.

intelfx ★★★★★
()