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),
}
}
}
When I try to compile my code, I get the following error:
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()
}
}
}
}