Reputation: 1
I have to add a constraint over a struct defined by me. In particular this struct works with a generic called T which has two types: Input and Output. I want to set a constraint on which the type of the Input is the same of the type of Output.
#[derive(Debug, Clone)]
pub struct Example<T>
{
... // fields of the struct
}
In other words since I work either with T::Input and T::Output, I have some cases in which the Output of a struct A is the Input of a struct B and clearly this is a problem in compilation time because the error I get is:
expected T::Input, found T::Output
so the arguments are incorrect.
Actually I understand what is the problem, but I do not know how to explain what I want to do to the compiler.
I have tried to add the constraints using the where
clause trying to specify that "T::Input = T::Output", but they did not work.
I tried to do something like this:
#[derive(Debug, Clone)]
pub struct Example<T>
where
T::Input: From<T::Output>,
T::Output: From<T::Input>
{
... // fields of the struct
}
Upvotes: 0
Views: 534
Reputation: 98358
As kmdreko's answer comments, the straightforward way to write this would be something like:
trait InOut {
type Input;
type Output;
}
struct Example<T> where T: InOut<Input = T::Output> {
...
}
Unfortunately this fails with that weird circular dependency error.
But sometimes, when you use an associated type in a generic type parameter, it is required to be written fully, or some ambiguity error happens. In this case, instead of T::Output
you need to write <T as InOut>::Output
:
trait InOut {
type Input;
type Output;
}
struct Example<T> where T: InOut<Input = <T as InOut>::Output> {
...
}
And this just works.
My guess about why the former fails and the latter works is the following: When you write T::Output
the compiler does not what trait this Output
comes from, so it has to check all the implemented traits, but wait... what traits does it implement? it It doesn't know, yet, so it enters an infinite loop. But if you write <T as InOut>::Output
it does not have to check all the traits, it just have to implement InOut
and that is already assumed when it is evaluating this condition, so all goes well.
Upvotes: 4
Reputation: 60051
Unfortunately, the naive solution doesn't work:
trait InOut {
type Input;
type Output;
}
struct Example<T> where T: InOut<Input = T::Output> {
...
}
error[E0391]: cycle detected when computing the bounds for type parameter `T`
--> src/lib.rs:6:42
|
6 | struct Example<T> where T: InOut<Input = T::Output> {
| ^^^^^^^^^
|
= note: ...which immediately requires computing the bounds for type parameter `T` again
note: cycle used when computing explicit predicates of `Example`
--> src/lib.rs:6:42
|
6 | struct Example<T> where T: InOut<Input = T::Output> {
| ^^^^^^^^^
However, I was able to make it work with an additional trait that implements the original trait if the types match:
trait InOut {
type Input;
type Output;
}
trait InAndOut: InOut<Input = Self::Item, Output = Self::Item> {
type Item;
}
impl<T, I> InAndOut for T where T: InOut<Input = I, Output = I> {
type Item = I;
}
struct Example<T> where T: InAndOut {
...
}
Full example available on the playground.
Upvotes: 1