¿Cómo hay una implementación conflictiva de `From` cuando se usa un tipo genérico?

Estoy tratando de implementar una enumeración de error que puede contener un error asociado con uno de nuestros rasgos como este:

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

También he intentado implementar el rasgo From para permitir la construcción de MyError partir de una instancia de Storage::Error :

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

( patio de recreo )

Sin embargo, esto no se puede comstackr:

 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` 

No entiendo por qué el comstackdor cree que esto ya se ha implementado. El mensaje de error me dice que ya hay una implementación de From<MyError> (que existe), pero no estoy tratando de implementar eso aquí – Estoy tratando de implementar From y MyError no es del mismo tipo que S::Error por lo que puedo ver.

¿Me estoy perdiendo algo fundamental para los generics aquí?

El problema aquí es que alguien puede implementar Storage para que la Imp From que ha escrito se solape con la impl en la biblioteca estándar de impl From for T (es decir, cualquier elemento se puede convertir a sí mismo).

Específicamente,

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

(La configuración aquí significa que esto realmente no comstack: MyError es infinitamente grande, pero ese error no está relacionado con el razonamiento sobre impl s / coherence / overlap, y de hecho pequeños cambios en MyError pueden hacer que se compile sin cambiar el problema fundamental, por ejemplo, agregar un Box como StorageProblem(Box), )

Si sustituimos Tricky en lugar de S en su impl, obtenemos:

 impl From> for MyError { ... } 

Esta impl coincide exactamente con la auto conversión con T == MyError , y por lo tanto el comstackdor no sabría cuál elegir. En lugar de hacer una elección arbitraria / aleatoria, el comstackdor Rust evita situaciones como esta, y por lo tanto, el código original debe ser rechazado debido a este riesgo.

Esta restricción de coherencia definitivamente puede ser molesta, y es una de las razones por las que la especialización es una característica muy esperada: esencialmente permite instruir manualmente al comstackdor sobre cómo manejar la superposición … al menos, una de las extensiones del formulario restringido actual permite eso.

Una solución para el problema de coherencia es usar Result::map_err para realizar la conversión usted mismo. ¡Entonces puede usar el Result final con try! o ? :

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

Esta solución también es valiosa cuando hay variantes de error que tienen el mismo Error subyacente, como si desea separar los errores de “apertura de archivo” y “lectura de archivo”, que son io::Error .