How to bound TryFrom Error for a generic argument?

I have a argument that implements a generic TryFrom and want to handle the error. How do I lock down that the Error implements debug so that, regardless, I am able to bubble up the error handling?:

pub async fn do_some<'a, T>(
    convertible: &'a T,
) -> Result<String, MyError>
where
    ConvertTo: TryFrom<&'a T>,
{
    let mut dat: ConvertTo = convertible.try_into().map_err(
      // but err doesn't implement anything since its type is unknown...
      |err| MyError::from(format!("{:?}", err))
    )?;
    todo()!
}

The problem is that Error is declared individually inside the TryFrom, so I couldn't do something like: TryFrom<&'aT, E: Debug>.

How can I decently convert that error into what my app needs?

The only workaround I can think of is calling TryInto() when the type is not generic, since then the Error is concretely known, but then I'm repeating myself everywhere that calls do_some

where
    ConvertTo: TryFrom<&'a T, Error: Debug>,

Or if you have a MSRV that's too low for that feature,

where
    ConvertTo: TryFrom<&'a T,>,
    <ConvertTo as TryFrom<&'a T>>::Error: Debug,
1 Like

this gets me a lot closer, but unless I manually map_err trying to use ? results in:

error[E0277]: `?` couldn't convert the error to `MyError`
  --> src/data.rs:29:58
   |
29 |     let mut dat = ConvertTo::try_from(convertible)?;
   |                                            ^ the trait `From<<data::ConvertTo as TryFrom<&'a T>>::Error>` is not implemented for `MyError`
   |
   = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait

 = note: required for `Result<std::string::String, MyError>` to implement `FromResidual<Result<Infallible, <data::ConvertTo as TryFrom<&'a T>>::Error>>`

I'm not sure how to implement that recommendation... When I tried it said it was "unstable"

You would need to have something like this:

impl<D: std::fmt::Debug> From<D> for MyError {

Then it should work... but it may make more sense to not add that implementation and to use .map_err instead, as unless your error type is completely text based, you're probably setting yourself up for implicit conversions with an incorrect context elsewhere.

Or maybe it makes sense to restrict the function instead, depending on the overall use or design at play in your code.

1 Like

lol, magic. How should I read the compiler output to know to implement Debug instead? Sometimes if I read the output literally I go about implementing something that can't be implemented (stable). In this case, how did you know that's what it actually meant?

Well, in short, I knew enough about how ? works.

I knew that roughly speaking[1] the ? operator on Result works like

match res {
    Ok(o) => o,
    Err(e) => return Err(<_>::from(e)),
}

For the first approach, the only thing you know about <data::ConvertTo as TryFrom<&'a T>>::Error are the bounds from the trait and from your own bounds: it implements Debug (and Sized). Otherwise, since this is a generic context, it could be anything. So if you want to make use of ?, you'd have to have the From implementation for everything that implements Debug (and Sized).

The second approach is to just put the necessary bound so that ? works on the function instead.

(Truth be told, I first tried

    ConvertTo: TryFrom<&'a T, Error: std::fmt::Debug + Into<MyError>>,

But that didn't work.)


It would be nice if the compiler said something like "MyError doesn't implement From<[the associated type]>" in this situation, or linked to ? docs etc.


  1. the actual mechanism is more abstract â†Šī¸Ž

2 Likes