1

I'm building myself a parsing library, and I found a key distinction between two types of declared trait bounds.

Take the following trait:

pub trait Parsable: FromStr<Err: Display>
{}

With this, the following function compiles and works just fine:

fn parse<T>(s: &str) -> Result<T, T::Err>
where
    T: Parsable
{
    s.parse()
}

However, if the Parsable trait is declared in this way:

pub trait Parsable: FromStr
where
    Self::Err: Display
{}

Then the above defined parse function fails to compile.

`<T as FromStr>::Err` doesn't implement `std::fmt::Display`
the trait `std::fmt::Display` is not implemented for `<T as FromStr>::Err`

You have to also explicitly constrain T::Err to Display within the declatation of parse like this:

fn parse<T>(s: &str) -> Result<T, T::Err>
where
    T: Parsable,
    T::Err: Display
{
    s.parse()
}

And only now does it actually compile.

My question is - why? What is the semantic difference between the two trait declarations?

1
  • 1
    This looks like a bug in the compiler to me. Commented Dec 17, 2024 at 17:40

1 Answer 1

2

You had me scratching my head for a minute there, as I didn't recognise your first syntax (that which is working):

pub trait Parsable: FromStr<Err: Display>
{}

It turns out that specifying bounds in associated type position (like Err: Display above) is a (relatively) new feature—it was stabilised in Rust 1.79. As the blog post states (emphasis added):

  • Supertraits - a bound specified via the new syntax is implied when the trait is used, unlike where clauses. Sample syntax: trait CopyIterator: Iterator<Item: Copy> {}.
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.