<p>Take <a href="https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.ienumerable-1?view=net-7.0" rel="noreferrer">C#'s <code>IEnumerable&lt;T&gt;</code></a> interface. It has a singular requirement:</p>
<pre class="lang-csharp prettyprint-override"><code>public interface IEnumerable&lt;out T&gt;
{
  IEnumerator&lt;T&gt; GetEnumerator();
}
</code></pre>
<p><code>IEnumerator&lt;T&gt;</code> simply provides a way to traverse a sequence -- the kind of thing needed for a foreach loop. The implementation of that is unimportant here.</p>
<p>Now let's consider the same thing in a trait-based language. Immediately you have a problem: you can't just return an enumerator, as that's not a type. You can't use an <code>impl Enumerator&lt;T&gt;</code> either, as there's no method body to determine the concrete type from. In this case you might be able to get away with a <code>dyn Enumerator&lt;T&gt;</code>, but that's less than ideal and won't work for every scenario.</p>
<p>One solution, taken by Swift for many years, is to not allow traits (&quot;protocols&quot; in Swift) to use generic type syntax at all, instead opting for inferred associated types. It simply had</p>
<pre class="lang-swift prettyprint-override"><code>public protocol Sequence {
  associatedtype Element
  associatedtype Iterator: IteratorProtocol
    where Iterator.Element == Element

  func makeIterator() -&gt; Iterator
}
</code></pre>
<p>This becomes difficult to graft generic support onto. If you naïvely call all associated types generic type args, suddenly <code>Array&lt;Int&gt;</code> and <code>Set&lt;Int&gt;</code> aren't a common type -- one is <code>Sequence&lt;Int, IndexingIterator&lt;Array&lt;Int&gt;&gt;&gt;</code> and the other is <code>Sequence&lt;Int, Set&lt;Int&gt;.Iterator&gt;</code>.</p>
<p>On the other hand, not supporting generics at all becomes difficult, because now your struct can't hold a <code>dyn Enumerable&lt;Int&gt;</code>. This leads to a proliferation of either unwanted generics or single-purpose box types like <a href="https://developer.apple.com/documentation/swift/anysequence" rel="noreferrer">Swift's <code>AnySequence&lt;Element&gt;</code></a>.</p>
<p>How could a language support generic traits without making them cumbersome to use?</p>

<types><generics><traits>
How could a language implement generic traits?
<h1>Primary associated types</h1>
<p>Swift added this in version 5.7, and it was a huge boon to the usability of protocols. With this syntax, you can pick and choose which associated types can act like generics:</p>
<pre class="lang-swift prettyprint-override"><code>public protocol Sequence&lt;Element&gt; {
  associatedtype Element
  associatedtype Iterator: IteratorProtocol
    where Iterator.Element == Element

  func makeIterator() -&gt; Iterator
}

// Swift 'some' and 'any' are equivalent to Rust 'impl' and 'dyn' respectively
let someIntegers: some Sequence&lt;Int&gt; = Array&lt;Int&gt;([1, 2, 3])
let iter1 = someIntegers.makeIterator() // inferred type: some IteratorProtocol&lt;Int&gt;
let whoKnowsWhat: any Sequence&lt;Int&gt; = Set&lt;Int&gt;([1, 2, 3])
let iter2 = whoKnowsWhat.makeIterator() as any IteratorProtocol // generic lost
</code></pre>
<p>One advantage this has is the fact that adding primary associated types to an existing protocol is non-breaking -- you can still have <code>some Sequence</code> and <code>any Sequence</code> without generic types.</p>
<p>This comes with a disadvantage that you have no idea what certain associated types are, and no way to specify. Also, as illustrated in the example above, sometimes these generics are lost (in this case, the <code>where</code> clause means it <em>could</em> be kept, but in the general case that's not always possible).</p>

CC BY-SA 4.0
2023-05-18T23:26:41.853Z
2023-05-18T23:26:41.853Z
Bbrk24
# How could a language implement generic traits?
Posted by: username_0 on 2023-05-18
Take C#'s IEnumerable interface. It has a singular requirement:
public interface IEnumerable
{
  IEnumerator GetEnumerator();
}

IEnumerator simply provides a way to traverse a sequence -- the kind of thing needed for a foreach loop. The implementation of that is unimportant here.
Now let's consider the same thing in a trait-based language. Immediately you have a problem: you can't just return an enumerator, as that's not a type. You can't use an impl Enumerator either, as there's no method body to determine the concrete type from. In this case you might be able to get away with a dyn Enumerator, but that's less than ideal and won't work for every scenario.
One solution, taken by Swift for many years, is to not allow traits ("protocols" in Swift) to use generic type syntax at all, instead opting for inferred associated types. It simply had
public protocol Sequence {
  associatedtype Element
  associatedtype Iterator: IteratorProtocol
    where Iterator.Element == Element

  func makeIterator() -> Iterator
}

This becomes difficult to graft generic support onto. If you naïvely call all associated types generic type args, suddenly Array and Set aren't a common type -- one is Sequence>> and the other is Sequence.Iterator>.
On the other hand, not supporting generics at all becomes difficult, because now your struct can't hold a dyn Enumerable. This leads to a proliferation of either unwanted generics or single-purpose box types like Swift's AnySequence.
How could a language support generic traits without making them cumbersome to use?
**Tags:** types generics traits
---
## 1 answers
### Answer by username_0 on 2023-05-18. Score: 2
Primary associated types
Swift added this in version 5.7, and it was a huge boon to the usability of protocols. With this syntax, you can pick and choose which associated types can act like generics:
public protocol Sequence {
  associatedtype Element
  associatedtype Iterator: IteratorProtocol
    where Iterator.Element == Element

  func makeIterator() -> Iterator
}

// Swift 'some' and 'any' are equivalent to Rust 'impl' and 'dyn' respectively
let someIntegers: some Sequence = Array([1, 2, 3])
let iter1 = someIntegers.makeIterator() // inferred type: some IteratorProtocol
let whoKnowsWhat: any Sequence = Set([1, 2, 3])
let iter2 = whoKnowsWhat.makeIterator() as any IteratorProtocol // generic lost

One advantage this has is the fact that adding primary associated types to an existing protocol is non-breaking -- you can still have some Sequence and any Sequence without generic types.
This comes with a disadvantage that you have no idea what certain associated types are, and no way to specify. Also, as illustrated in the example above, sometimes these generics are lost (in this case, the where clause means it could be kept, but in the general case that's not always possible).
---
