Как происходит конфликтная реализация `From` при использовании родового типа?

Я пытаюсь реализовать перечисление ошибок, которое может содержать ошибку, связанную с одной из наших характеристик:

trait Storage { type Error; } enum MyError { StorageProblem(S::Error), } 

Я также попытался реализовать свойство From чтобы разрешить создание MyError из экземпляра Storage::Error :

 impl From for MyError { fn from(error: S::Error) -> MyError { MyError::StorageProblem(error) } } 

( детская площадка )

Однако это не скомпилируется:

 error[E0119]: conflicting implementations of trait `std::convert::From<MyError>` for type `MyError`: --> src/main.rs:9:1 | 9 | / impl From for MyError { 10 | | fn from(error: S::Error) -> MyError { 11 | | MyError::StorageProblem(error) 12 | | } 13 | | } | |_^ | = note: conflicting implementation in crate `core` 

Я не понимаю, почему компилятор считает, что это уже реализовано. Сообщение об ошибке сообщает мне, что уже существует реализация From<MyError> (которая есть), но я не пытаюсь реализовать это здесь – я пытаюсь реализовать From и MyError не тот же тип, что и S::Error из того, что я вижу.

Здесь я пропустил нечто принципиальное для дженериков?

Проблема здесь заключается в том, что кто-то может реализовать Storage так что в написанном вами наложении от impl impl с помощью impl в стандартной библиотеке impl From for T (т. impl From for T Все может быть преобразовано в себя).

В частности,

 struct Tricky; impl Storage for Tricky { type Error = MyError; } 

(Настройка здесь означает, что это на самом деле не компилируется. MyError бесконечно велик, но эта ошибка не связана с аргументами в отношении s / coherence / overlap, и действительно небольшие изменения в MyError могут заставить его скомпилировать без изменения основная проблема, например добавление Box например StorageProblem(Box), .)

Если мы заменим Tricky вместо S в вашем impl, мы получим:

 impl From> for MyError { ... } 

Это impl точно соответствует самопереведению с T == MyError , и, следовательно, компилятор не знает, какой из них выбрать. Вместо произвольного / случайного выбора компилятор Rust избегает подобных ситуаций, и поэтому исходный код должен быть отклонен из-за этого риска.

Это ограничение связности может определенно быть раздражающим и является одной из причин того, что специализация является долгожданной особенностью: по существу позволяет вручную инструктировать компилятор, как обрабатывать перекрытие … по крайней мере, одно из расширений к текущей ограниченной форме позволяет это.

Result::map_err для проблемы согласованности является использование Result::map_err для выполнения преобразования самостоятельно. Затем вы можете использовать результат завершения с помощью try! или ? :

 fn example(s: S) -> Result> { s.do_a_thing().map_err(MyError::StorageProblem)?; Ok(42) } 

Это решение также полезно, когда существуют варианты ошибок, которые имеют одну и ту же базовую Error , например, если вы хотите отделить ошибки «открытия файла» и «чтения файла», оба из которых – io::Error .

  • Почему Rust не поддерживает повышение уровня объекта?
  • Есть ли способ создать псевдоним типа для нескольких признаков?
  • Не удается выйти из заемного контента при развертывании
  • Как включить модуль из другого файла из того же проекта?
  • Println! error: ожидается, что аргумент literal / format должен быть строковым литералом
  • Пакет ржавчины с библиотекой и двоичным?
  • Почему законно занимать временное?
  • Как избежать дублирования функций доступа для изменяемых и неизменных ссылок в Rust?
  • Могу ли я делать интроспекцию типа с объектами признаков, а затем опускать ее?
  • Каков тип возврата операции индексирования?
  • Как передать непересекающиеся fragmentы из вектора в разные streamи?
  • Давайте будем гением компьютера.