Here's the code I would like to compile:
struct Error {}
trait Foo {
type FooError;
fn fallible_function() -> Result<(), Self::FooError>;
}
trait SubFoo: Foo
where
Error: From<<Self as Foo>::FooError>,
{
}
fn some_function<T: SubFoo>(object: T) {}
Unfortunately, it won't compile without repeating the From
trait bound under some_function
, like this:
fn some_function<T: SubFoo>(object: T)
where
Error: From<<T as Foo>::FooError>,
{
}
That defeats the point though, as the point of SubFoo
is to reduce the boilerplate on the many points where its fully described supertraits would be used (you can imagine this is a lot more bulky in my real code than in this simplified snippet). I'm aware the reason why this doesn't work is this issue, which appears to be a little bit stuck in limbo (with the possible implication that nothing you could ever express here is inexpressible with normal trait bounds, outside a where
clause?)
Since the above is a no-go until that issue gets closed, I'm looking for alternative solutions to my problem. Pulling the associated_type_bounds
unstable feature, at least I'm able to express a similar constraint:
#![feature(associated_type_bounds)]
struct Error {}
trait Foo {
type FooError;
fn fallible_function() -> Result<(), Self::FooError>;
}
trait SubFoo: Foo<FooError: Into<Error>> {}
This is almost fine (and to be fair, looks a lot more elegant than the where
clause before). However, it won't cut it for two reasons:
- Where
From<A> for B
impliesInto<B> from A
, the reverse is not true. - The
?
operator requiresFrom<FooError> for Error
I can't think of a way to encode the From<FooError> for Error
requirement in the supertrait bound without using a where
clause. Am I missing a key bit of syntax here, or am I out of luck?
I can't do this:
trait Foo
where
Error: From<Self::FooError>,
{
type FooError;
fn fallible_function() -> Result<(), Self::FooError>;
}
In my real code, this would break encapsulation (the module where Foo lives has no reason to see the top level Error struct).