Как происходит конфликтная реализация `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) } }
( детская площадка )
- Как создать глобальный, изменяемый синглтон?
- Возrotation замыкания из функции
- Преобразовать строку в int в Rust?
- Почему Iterator :: take_while получает право собственности на iterator?
- Как проверить равенство между объектами признаков?
Однако это не скомпилируется:
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
из того, что я вижу.
Здесь я пропустил нечто принципиальное для дженериков?
- Как программа Rust может получать доступ к метаданным из своего пакета Cargo?
- Внедрение признака для нескольких типов одновременно
- Как преобразовать String в & 'static str
- Как мне передать изменяемый объект между streamами?
- Вектор объектов, принадлежащих признаку
- Как увидеть расширенный макрокод, вызывающий ошибку компиляции?
- Почему этот шаблон совпадения недоступен при использовании нелитеральных шаблонов?
- Инициализировать большой массив фиксированного размера с типами без копирования
Проблема здесь заключается в том, что кто-то может реализовать Storage
так что в написанном вами наложении от impl impl с помощью impl в стандартной библиотеке impl
(т. impl
Все может быть преобразовано в себя).
В частности,
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
.