genericsrustanyhow

Formatting errors in a generic TryInto implementation


I am trying to make a generic implementation of TryFrom for an object T whenever T implements a TryFrom. However, I am struggling to pass anyhow::Errors.

In the code below, I can convert a Generic<T> into a ValueContainer so long as &usize: TryFrom<T>. It almost does what I want, but does not allow me to properly handle errors from the &usize: TryFrom<T>.

#[cfg(test)]
mod question {
    use std::fmt::Debug;

    use anyhow::anyhow;

    #[derive(Debug)]
    struct Generic<T: Debug>(T);
    #[derive(Debug)]
    struct Foo(usize);
    #[derive(Debug)]
    struct ValueContainer<'a>(&'a usize);

    impl TryFrom<Foo> for &usize {
        type Error = anyhow::Error;

        fn try_from(value: Foo) -> Result<Self, Self::Error> {
            if value.0 > 5 {
                Ok(&5)
            } else {
                Err(anyhow!("Value must be bigger"))
            }
        }
    }

    impl<'a, T> TryFrom<Generic<T>> for ValueContainer<'a>
    where
        for<'b> &'b usize: TryFrom<T>,
        T: Debug,
        for<'b> anyhow::Error: From<<&'b usize as TryFrom<T>>::Error>,
    {
        type Error = anyhow::Error;

        fn try_from(value: Generic<T>) -> Result<Self, Self::Error> {
            // XXX: Can't do this?
            // let value: &usize = value.0.try_into().unwrap();
            match <&usize as TryFrom<T>>::try_from(value.0) {
                Ok(v) => Ok(ValueContainer(v)),
                Err(_) => {
                    // XXX: I would like this to be the error from the try_into above
                    // but there is an error `<&usize as TryFrom<T>>::Error` cannot be formatted with the default formatter`
                    return Err(anyhow!("Error converting type"));
                }
            }
        }
    }

    #[test]
    fn test_try_into() {
        let value = Generic(Foo(10));
        let value: Result<ValueContainer, anyhow::Error> = value.try_into();
        dbg!(&value);
        let value = Generic(Foo(2));
        let value: Result<ValueContainer, anyhow::Error> = value.try_into();
        dbg!(&value);
    }
}

I am not able to use value.try_into()? in my implementation of TryFrom<Generic<T>>. Uncommenting the let value: &usize = value.try_into()?; produces the error

error[E0271]: type mismatch resolving `<&usize as TryFrom<Generic<T>>>::Error == <&usize as TryFrom<T>>::Error`
   --> lib-doenetml-core/src/core/props/prop_view.rs:140:40
    |
140 |              let value: &usize = value.try_into()?;
    |                                        ^^^^^^^^ expected `Infallible`, found associated type
    |
    = note:         expected enum `Infallible`
            found associated type `<&usize as TryFrom<T>>::Error`
    = help: consider constraining the associated type `<&usize as TryFrom<T>>::Error` to `Infallible`
    = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html

It seems like Using TryInto generics with anyhow::Error might be relevant, and it inspired me to add for<'b> anyhow::Error: From<<&'b usize as TryFrom<T>>::Error>, but that didn't seem to work...

Any ideas on how I can allow value.try_into()? directly?


Solution

  • ? will work if you will fix your typo and add .0.

    unwrap() requires the Debug trait for the error (because it prints it on failure), so you need to add this bound:

    impl<'a, T> TryFrom<Generic<T>> for ValueContainer<'a>
    where
        for<'b> &'b usize: TryFrom<T>,
        T: Debug,
        for<'b> anyhow::Error: From<<&'b usize as TryFrom<T>>::Error>,
        for<'b> <&'b usize as TryFrom<T>>::Error: Debug,
    {
        type Error = anyhow::Error;
    
        fn try_from(value: Generic<T>) -> Result<Self, Self::Error> {
            // XXX: Can't do this?
            let value: &usize = value.0.try_into().unwrap();
            Ok(ValueContainer(value))
        }
    }