Extension on a protocol where associated type is constrained using a same-type requirement to be a generic struct

I hope this is the correct subforum, it’s about fixing a bug in stdlib, but it’s a technical question about using swift!

I started to look at SR-7266 over the weekend, which is to handle reversing a ReversedCollection, so that it returns the original base collection rather than ReversedCollection<ReversedCollection<Base>>. I have this part working fine with minimal additions, but I’m having problems when it comes to LazyCollectionProtocol which also defines a reverse function, which would be good to handle in a similar fashion.

I have a version working which adds a ReversedCollectionProtocol to allow constraining an extension on LazyCollectionProtocol, but it’s not ideal because it adds a new protocol to the mix. This can be found in this gist, which can be run from a playground.

If you provide ReversedCollection as a same-type requirement for Elements, Swift requires the generic type to be specific. I tried using BidirectionalCollection:

extension LazyCollectionProtocol
  where
  Self: BidirectionalCollection,
  Elements == ReversedCollection<BidirectionalCollection>

but I get the error:

using ‘BidirectionalCollection’ as a concrete type conforming to protocol ‘BidirectionalCollection’ is not supported

I’m not too sure how else I can constrain this extension. I think ideally it’d be nice to add a new generic into the mix, something like the following:

extension LazyCollectionProtocol
  where
  Self: BidirectionalCollection,
  Elements == ReversedCollection<T> {

  // Reversing a lazy reversed collection returns a lazy representation of the original collection.
  public func reversed() -> LazyCollection<T> {
    return elements._base.lazy
  }
}

but unsurprisingly this gives an an error

use of undeclared type ‘T’

I’ve looked through the Language Reference section in the Swift book (for version 4.1) around Extensions and Generic Where Clauses to see if there was anything in the language that I was missing that could help add this constraint, but I can’t find anything there.

Is there anything I’m missing here? Thanks!

You would need to parametrize the extension… Parametrized extensions are in the Generics Manifesto, but for non-protocol types AFAIK. Either way, you will have to wait for them. Meanwhile,

If Self and T (from ReversedCollection<T: BidirectionalCollection>) are of the same type in your case, this will be enough:

extension LazyCollectionProtocol where 
Self: BidirectionalCollection, 
Elements == ReversedCollection<Self> {
    
}

Otherwise you can use generic functions as a workaround:

extension LazyCollectionProtocol where Self: BidirectionalCollection {

    func foo<T: BidirectionalCollection>(...) -> ... where Self.Elements == ReversedCollection<T> {...}
}
1 Like