Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Avoiding recursion in std::convert::TryFrom implementation for a custom enum with nested values

I’m trying to implement the std::convert::TryFrom trait for a custom enum called DashNumber. This enum represents numbers with infinity and negative infinity, and I want to be able to convert it to a usize for any type T. However, when I implement the conversion for DashNumber<T> to usize, it also implements it for DashNumber<DashNumber<T>> and so on recursively. How can I stop this recursion and only implement the conversion for the base case DashNumber<T>?

use core::*;
enum DashNumber<N> {
    NegInfinity,
    Number(N),
    Infinity,
}
#[derive(Debug)]
enum TryFromDashNumberError<T: std::fmt::Debug>
where
    usize: TryFrom<T>,
    <usize as TryFrom<T>>::Error: std::error::Error + 'static,
{
    NegInfinity,
    Number(<usize as TryFrom<T>>::Error),
    Infinity,
}
impl<T> fmt::Display for TryFromDashNumberError<T>
where
    usize: TryFrom<T>,
    <usize as TryFrom<T>>::Error: std::error::Error + 'static,
    T: std::fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            TryFromDashNumberError::<T>::NegInfinity => {
                write!(f, "Negative infinity cannot be converted to a usize")
            }
            TryFromDashNumberError::<T>::Number(e) => {
                write!(f, "Error converting number to usize: {}", e)
            }
            TryFromDashNumberError::<T>::Infinity => {
                write!(f, "Infinity cannot be converted to a usize")
            }
        }
    }
}

impl<T> TryFrom<DashNumber<T>> for usize
where
    usize: TryFrom<T>,
    <usize as TryFrom<T>>::Error: std::error::Error + 'static,
    T: std::fmt::Debug,
{
    type Error = TryFromDashNumberError<T>;

    fn try_from(value: DashNumber<T>) -> Result<Self, Self::Error> {
        match value {
            DashNumber::NegInfinity => Err(TryFromDashNumberError::<T>::NegInfinity),
            DashNumber::Number(n) => {
                usize::try_from(n).map_err(|e| TryFromDashNumberError::<T>::Number(e))
            }
            DashNumber::Infinity => Err(TryFromDashNumberError::<T>::Infinity),
        }
    }
}

playground link

When I try to compile my code, I get the following error:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

error[E0275]: overflow evaluating the requirement `usize: TryFrom<_>`
  --> src/lib.rs:7:10
   |
7  | #[derive(Debug)]
   |          ^^^^^
   |
   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`playground`)
note: required for `usize` to implement `TryFrom<DashNumber<_>>`
  --> src/lib.rs:38:9
   |
38 | impl<T> TryFrom<DashNumber<T>> for usize
   |         ^^^^^^^^^^^^^^^^^^^^^^     ^^^^^
   = note: 127 redundant requirements hidden
   = note: required for `usize` to implement `TryFrom<DashNumber<..nested DashNumber 125 times>>`
   = note: this error originates in the derive macro `Debug` (in Nightly builds, run with -Z macro-backtrace for more info)

For more information about this error, try `rustc --explain E0275`.

Any ideas on how to fix this?

>Solution :

The error comes from the <usize as TryFrom<T>>::Error: std::error::Error + 'static on the enum definition. We can remove that among other bounds, and it works:

use core::fmt;
use std::error::Error;

enum DashNumber<N> {
    NegInfinity,
    Number(N),
    Infinity,
}

enum TryFromDashNumberError<T>
where
    usize: TryFrom<T>,
{
    NegInfinity,
    Number(<usize as TryFrom<T>>::Error),
    Infinity,
}
impl<T> fmt::Display for TryFromDashNumberError<T>
where
    usize: TryFrom<T>,
    <usize as TryFrom<T>>::Error: Error,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            TryFromDashNumberError::NegInfinity => {
                write!(f, "Negative infinity cannot be converted to a usize")
            }
            TryFromDashNumberError::Number(e) => {
                write!(f, "Error converting number to usize: {}", e)
            }
            TryFromDashNumberError::Infinity => {
                write!(f, "Infinity cannot be converted to a usize")
            }
        }
    }
}

impl<T> TryFrom<DashNumber<T>> for usize
where
    usize: TryFrom<T>,
    <usize as TryFrom<T>>::Error: Error,
{
    type Error = TryFromDashNumberError<T>;

    fn try_from(value: DashNumber<T>) -> Result<Self, Self::Error> {
        match value {
            DashNumber::NegInfinity => Err(TryFromDashNumberError::<T>::NegInfinity),
            DashNumber::Number(n) => {
                usize::try_from(n).map_err(|e| TryFromDashNumberError::<T>::Number(e))
            }
            DashNumber::Infinity => Err(TryFromDashNumberError::<T>::Infinity),
        }
    }
}

You’ll also need to implement Debug manually, since the derive macro will always add an unnecessary bound T: Debug and will not include the <usize as TryFrom<T>>::Error: Debug bound you really need:

impl<T> fmt::Debug for TryFromDashNumberError<T>
where
    usize: TryFrom<T>,
    <usize as TryFrom<T>>::Error: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            TryFromDashNumberError::NegInfinity => {
                f.debug_tuple("NegInfinity").finish()
            }
            TryFromDashNumberError::Number(e) => {
                f.debug_tuple("Infinity").field(&e).finish()
            }
            TryFromDashNumberError::Infinity => {
                f.debug_tuple("Infinity").finish()
            }
        }
    }
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading