简体   繁体   中英

Requiring a trait bound on the associated type of an inherited trait

I have a trait Foo inheriting from another trait Bar . Bar has an associated type Baz . Foo constrains Baz such that Baz must implement Hoge .

trait Hoge {}

trait Bar {
    type Baz;
}

trait Foo: Bar where Self::Baz: Hoge {}

However, when I define a generic function requiring the generic type T to implement Foo ,

// [DESIRED CODE]
fn fizz<T: Foo>(buzz: T) {
    // ...
}

rustc complains with EO277 unless I constrain T explicitly:

fn fizz<T: Foo>(buzz: T) where T::Baz: Hoge {
    // ...
}

I do not understand why I need to do this. I would like to be able to write [DESIRED CODE] . What is the recommended way to do this?

Sadly (or not), you have to repeat the bounds.

Last year I opened a issue thinking that the type checker was being inconsistent. The code is similar to yours.

@arielb1 closed the issue and said that this was the intended behavior and gave this explanation:

The thing is that we don't want too many bounds to be implicitly available for functions, as this can lead to fragility with distant changes causing functions to stop compiling. There are basically 3 kinds of bounds available to a function:

  • bounds from explicit where-clauses - eg T: B when you have that clause. This includes the "semi-explicit" Sized bound.
  • bounds from supertraits of explicit where-clauses - a where-clause adds bounds for its supertraits (as trait B: A , the T: B bound adds a T: A bound).
  • bounds from the lifetime properties of arguments (outlives/implicator/implied bounds). These are only lifetime bounds, and irrelevant for the current problem. rust-lang/rfcs#1214 involved them a great deal.

If your bound isn't in the list, you will have to add it explicitly if you want to use it. I guess this should be a FAQ entry.

Today I opened an issue to request that this information to be added to the docs.

It is possible to work around this behaviour by using another associated type in Foo since the compiler accepts implicit bounds when part of the associated type definition ( playground ):

trait Foo: Bar<Baz = Self::HogeBaz> {
    type HogeBaz: Hoge;
}

The new associated type can be hidden in a helper trait to avoid having to include it in every implementation. Full example (with renaming for clarity) ( playground )

trait Bound {
    fn bound();
}

trait Trait {
    type Type;
}

trait BoundedTypeHelper: Trait<Type = Self::BoundedType> {
    type BoundedType: Bound;
}

impl<T> BoundedTypeHelper for T
where
    T: Trait,
    Self::Type: Bound,
{
    type BoundedType = Self::Type;
}

trait UserTrait: BoundedTypeHelper {}

fn fizz<T: UserTrait>() {
    T::Type::bound()
}

I am with you in thinking that the original where-based bound ought to be treated as part of the trait definition and applied implicitly. It feels very arbitrary that bounding associated types inline works but where clauses do not.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM