What is the syntax for restricting a generic constraint to an associated type?

What is the correct syntax for indicating that a generic type should at least conform to an associated type?

protocol ListDescriptor {
  associatedtype ItemType: Hashable

  var items: [ItemType] { get }
}

struct SimpleList<ItemType>: ListDescriptor where ItemType == ListDescriptor.ItemType {
  var items: [ItemType] = []
}

Error:
Associated type 'ItemType' can only be used with a concrete type or generic parameter base

There might be several constraints on the protocol's associated type that need to be adhered to by the generic types in a class or struct. How do I effectively "bubble up" this information?

You don't need the where clause (or at least, not in this form); all you need is to constrain ItemType to Hashable:

protocol ListDescriptor {
  associatedtype ItemType: Hashable

  var items: [ItemType] { get }
}

struct SimpleList<ItemType: Hashable>: ListDescriptor {
  var items: [ItemType] = []
}

If I'm not mistaken the lookup for associated types is performed based on their names, so you don't have to explicitly bind them to a protocol's associated types. If you want to choose a different name for the ItemType generic parameter, you will need to typealias it to match the name of the associated type in the protocol.

2 Likes

In my basic example, then explicitly setting Hashable is viable, but in an application I'm working on, there are several constraints that end up propagating several levels "deep". I could go through them all and annotate them appropriately, but then making a change because a pain because I have to go back through and find all the "broken" ones.

For example, SimpleList has a property for configuring layout which in term is also generic over ItemType while a property inside the layout property is also generic over `ItemType. That might leave me with:

  • SimpleList<ItemType>
    • GridLayout<ItemType>
      • SectionLayout<ItemType>
        • ItemLayout<ItemType>

I'm curious if there's a better way to constrain each ItemType to the ItemType of it's parent class or protocol. In this case, all the ItemType constraints should be the same.

The best I've come up with is to create yet another protocol and use that everywhere. Something like:

protocol ListableItem: Hashable {}

...and then to use ListableItem as a constraint everywhere I need it, but I was wondering what other options might exist.

I cannot quite grasp your goal, but normally you don't have to do anything complicated if you want to ensure equality of generic parameters. For instance, in

struct MyStruct<T: Hashable> {
    var array: [[[T]]] // Equal to Array<Array<Array<T>>>
}

the generic parameter T will be the same for MyStruct and the deeply nested array at any point of use; the same happens with any generic types (i.e. not only arrays) you use within a type, as long as you use the same name for the generic parameter(s). It will also automatically be Hashable, as otherwise it can't be used within MyStruct.

So above, the nested array's T is effectively constrained to be equal to T of MyStruct with all of its extra requirements, and there shouldn't be any extra work for you to do, unless I misunderstand the question.

Terms of Service

Privacy Policy

Cookie Policy