3

In this following code snippet, convert is trying to convert its input to a Box<Error>:

fn convert<T: Into<Box<Error>>>(input: T) -> Box<Error> {
    input.into() // Compiles
    // From::from(input) // Fails to compile
}

It works with input.into(), but when using From::from(T) it no longer works, it requires T to implement Error:

error[E0277]: the trait bound `T: std::error::Error` is not satisfied
 --> src/main.rs:4:3
  |
4 |   From::from(input)
  |   ^^^^^^^^^^ the trait `std::error::Error` is not implemented for `T`
  |
  = help: consider adding a `where T: std::error::Error` bound
  = note: required because of the requirements on the impl of `std::convert::From<T>` for `std::boxed::Box<std::error::Error>`
  = note: required by `std::convert::From::from`

Why do the requirements change when using From or Into? This becomes specially annoying when using the ? operator:

fn convert<T: Into<Box<Error>>>(input: T) -> Result<(), Box<Error>> {
    Err(input)? // Fails to compile
}

Is there any way to use the ? operator properly in these cases, or do I have to resort to manual match and into?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
negamartin
  • 885
  • 8
  • 12

2 Answers2

9

Why do the requirements change when using From or Into?

Even when something implements Into, that doesn't mean it implements From. For example, from the implementor list of Into:

impl<T, U> Into<U> for T 
where
    U: From<T>, 

impl Into<Option<P<PathParameters>>> for AngleBracketedParameterData
impl Into<Vec<Annotatable>> for Annotatable
impl<T> Into<Vec<T>> for ThinVec<T>
impl<T> Into<Vec<T>> for P<[T]>

Those last four types do not implement From, but they do implement Into. (Note that these types are actually internal compiler types and it's a bug that we can see them in the docs, but it's a useful demonstration for this case.)

See When should I implement std::convert::From vs std::convert::Into? for more details about the differences of the two traits.


You can instead declare that your generic type implements From to use it:

fn convert<T>(input: T) -> Box<Error>
where
    Box<Error>: From<T>,
{
    From::from(input) // Fails to compile
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 2
    The converse, however, is true: if `U` implements `From`, then `T` *does* implement `Into` (because of the blanket impl of [`Into`](https://doc.rust-lang.org/std/convert/trait.Into.html)). The asymmetry of the relationship can be confusing. – trent Sep 15 '17 at 19:10
  • Oh, I see now, thanks. I assumed the From <-> Into relationship was both ways. It's nice you can place constraints with generics on the right hand side too! – negamartin Sep 19 '17 at 23:08
4

One solution would be to write your generics in terms of From:

fn convert<T>(input: T) -> Result<(), Box<Error>>
where
    Box<Error>: From<T>,
{
    Err(input)?
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Lukazoid
  • 19,016
  • 3
  • 62
  • 85