Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift protocol with associated type - type may not reference itself as a requirement

I have the following protocol and its extension

public protocol RESEndpointReachable: CustomDebugStringConvertible
{
    associatedtype EndpointType: RESEndpointReachable


    //  MARK: - Properties

    /// The name of the endpoint as defined in the REST URI.
    var name: String { get }

    /// An array of possible next endpoints that this endpoint can reach. E.g account's next endpoints would be authenticate and unauthenticate.
    var nextPossibleEndpoints: [EndpointType] { get }


    //  MARK: - Ability

    /// Used to process the endpoint.
    func processRequest(request: RERequest)

    /// Processes the next endpoint that matches the name `name`. Expects an endpoint with the name `name` to exist in `nextPossibleEndpoints`.
    func processNextEndpointWithName(name: String, request: RERequest)
}

public extension RESEndpointReachable
{
    //  MARK: - CustomDebugStringConvertible

    public var debugDescription: String {
        return name
    }


    //  MARK: - RESEndpointReachable

    var nextPossibleEndpoints: [EndpointType] {
        return []
    }

    public func processRequest(request: RERequest)
    {
        //  Check all possible endpoints are being processed
        if let nextEndpoint = nextPossibleEndpoints.first
        {
            fatalError("Unhandled endpoint \(nextEndpoint).")
        }
    }

    public func processNextEndpointWithName(name: String, request: RERequest)
    {
        //  Get the next endpoint that matches the specified name
        let nextEndpoints = nextPossibleEndpoints.filter { $0.name == name }

        if nextEndpoints.count > 1
        {
            fatalError("Multiple next endpoints found with the name '\(name)'.")
        }

        guard let nextEndpoint = nextEndpoints.first else
        {
            fatalError("No next endpoint with the name '\(name)'.")
        }


        //  Process the next endpoint
        nextEndpoint.processRequest(request)
    }
}

On building, the line associatedtype EndpointType: RESEndpointReachable has the following error: Type may not reference itself as a requirement. But as I understand it this is how you use associated types in Swift.

As you may have guessed, I always want whatever EndpointType ends up being set as to be a type that inherits from RESEndpointReachable.

like image 638
Adam Carter Avatar asked May 16 '16 11:05

Adam Carter


2 Answers

This feature is referred to by the Swift team as 'recursive protocol constraints', and is on the roadmap to be added in a future version of Swift. For more information about this and other planned features, check out the Swift team's 'Completing Generics' manifesto.

like image 58
AustinZ Avatar answered Sep 28 '22 06:09

AustinZ


Yep, that is not OK with swift. A protocol can't use it self as a type constraint. One solution is to declare an extra protocol that RESEndpointReachable itself will adopt and constraint RESEndpointReachable to that super protocol.

I took the example from the book iOS 10 Programming Fundamentals with Swift by Neuburg M. (please see page 194)

illegal example:

  1 protocol Flier {
  2         associatedtype Other : Flier
  3         func flockTogetherWith(_ f: Other)
  4 }
  5 
  6 struct Bird : Flier {
  7         func flockTogetherWith(_ f: Bird) {}
  8 }

Solution:

  1 protocol Superflier {}
  2 protocol Flier: Superflier {
  3         associatedtype Other : Superflier
  4         func flockTogetherWith(_ f: Other)
  5 }       
  6 
  7 struct Bird : Flier {
  8         func flockTogetherWith(_ f: Bird) {}
  9 } 

Cheers

like image 44
SLN Avatar answered Sep 28 '22 04:09

SLN