Specify constraint only once

trait Foo {
    type FooBar;
}

struct Bar<T: Foo> {
    foo: T,
    foobar: T::FooBar,
}

impl<T: Foo> Bar<T> {}

In the example above I have to specify that T must implement the Foo twice, which is not really handy when a constraint is more complicated. Is it possible to avoid this and specify the constraint only once?

I can imagine doing something like this:

trait Foo {
    type FooBar;
}

struct Bar<TFoo, TFooBar> {
    foo: TFoo,
    foobar: TFooBar,
}

impl<T: Foo> Bar<T, T::FooBar> {}

But I do not like this either, because that makes type parameter names inconsistent between struct and impl, which can be misleading.

Normally the answer would be "just don't specify it on the struct, and only on the impl block" like this:

struct Bar<T> {
    foo: T,
    foobar: T::FooBar,
}

But this doesn't work when you want to use an associated type such as T::FooBar. There's no way to avoid it, other than having two generic arguments like you've already figured out. That said, you could write your second example like this:

struct Bar<TFoo, TFooBar> {
    foo: TFoo,
    foobar: TFooBar,
}

impl<TFoo: Foo<FooBar = TFooBar>, TFooBar> Bar<TFoo, TFooBar> {}

or

impl<TFoo, TFooBar> Bar<TFoo, TFooBar>
where
    TFoo: Foo<FooBar = TFooBar>
{}

None of these options are great. My suggestion is to just deal with it and use the first option where you specify it twice.

2 Likes

One solution to this that is in the works (i. e. exists an unstable feature on nightly) is a feature for defining a short alias for a lengthy constraint.

2 Likes

You want the implied-bounds RFC 🔬 Tracking issue for RFC 2089: Extended Implied bounds · Issue #44491 · rust-lang/rust · GitHub

2 Likes