Home > Blockchain >  Using TryInto generics with anyhow::Error
Using TryInto generics with anyhow::Error

Time:06-15

The function is expected to accept a command which can be converted to a string, and also I am using anyhow to handle the error thing. Here is the code:

pub fn converter<T: TryInto<String>>(input: T) -> Result<String>
where
    T::Error: Into<anyhow::Error>,
{
    // error here
    // the trait bound `<T as TryInto<std::string::String>>::Error: StdError` is not satisfied
    // required because of the requirements on the impl of `From<<T as TryInto<std::string::String>>::Error>` for `anyhow::Error`
    // required because of the requirements on the impl of `FromResidual<Result<Infallible, <T as TryInto<std::string::String>>::Error>>` for `Result<std::string::String, anyhow::Error>
    let out_str = input.try_into()?;
    Ok(out_str)
}

I have added a type restriction for T::Error as T::Error: Into<anyhow::Error>, hope the compiler can do the error handling, but shows the T::Error can't be convert to a anyhow::Error.

The type restriction is referred from: https://github.com/dtolnay/anyhow/issues/172, but I still can't figure out how to use the TryInto generic with anyhow.

CodePudding user response:

TL;DR: Replace T::Error: Into<anyhow::Error> with anyhow::Error: From<T::Error>.


This one is quite subtle.

You might have expected the desugaring of ? to be like (ignoring extra facility like the Try trait):

let out_str = match input.try_into() {
    Ok(v) => v,
    Err(e) => return Err(Into::into(e)),
};

But it is not. Rather, it is more like:

let out_str = match input.try_into() {
    Ok(v) => v,
    Err(e) => return Err(From::from(e)),
};

Which produce the exact same error(s). This is because Result uses From to convert the error, not Into.

Now, having a From impl implies having an Into impl - because of the blanket implementation impl<T, U: From<T>> Into<U> for T - but the opposite is not true, and moreover, cannot be true with the current Rust trait system.

If Result would use Into in the desugaring, you'd have been fine, because the compiler can prove T::Error: Into<anyhow::Error> holds from your where clause, but given it uses From, the compiler cannot prove anyhow::Error: From<T::Error> holds, and thus raises an error.

There was an attempt to change the desugaring in the past (#60796), but unfortunately it caused too much inference breakage, so it is probably impossible by now. See:

CodePudding user response:

First, your example is missing a use anyhow::Result, because Result without a use is std::result::Result, which takes two generic parameters.

The reason why it doesn't compile is because Into can be derived from From, but From cannot be derived from Into. And the ? operator seems to use From.

Versions which do compile:

where
    T::Error: std::error::Error   Send   Sync   'static,
where
    anyhow::Error: From<T::Error>,
  • Related