3
votes

In Swift 2 I have the following protocols

protocol Fightable {
    // return true if still alive, false if died during fight
    func fight (other: Fightable) -> Bool
}

protocol Stats {
    var defense: Int { get }
    var attack: Int { get }
}

I can implement a protocol extension for Fightable to provide a shared implementation of fight across all value types which conform to Stats if I change the type signature of fight to

func fight (other: Self) -> Bool

and implement an extension as

extension Fightable where Self : Stats {
    func fight (other: Self) -> Bool {
        return self.defense > other.attack
    }
}

The problem with the above implementation is that it requires the value types to be the same (Humans can't fight Goblins). My current goal is to implement a protocol extension that provides a default implementation of fight for any combination of value types as long as they implement Stats.

The following code

extension Fightable where Fightable : Stats {
    func fight (other: Fightable) -> Bool {
        return self.defense > other.attack
    }
}

Produces the error

Type 'Fightable' in conformance requirement does not refer to a generic parameter or associated type

How can I make sure the other Fightable type also conforms to Stats for this extension?

I'm using Xcode 7 beta 1.

2

2 Answers

1
votes

I'm sorry but I misunderstood your problem. So if I understand you right (hopefully) it is impossible to get a default implementation of the fight function by a protocol extension (at least with these constraints). Because if you want other conform to Fightable and Stats it isn't the previous function anymore where other could be any Fightable. So it doesn't implement the required function. As workaround I would suggest (taking your existing code):

protocol Fightable {
    // return true if still alive, false if died during fight
    func fight (other: Fightable) -> Bool
}

protocol Stats {
    var defense: Int { get }
    var attack: Int { get }
}

extension Fightable where Self : Stats {
    // directly conforming to the protocol
    func fight (other: Fightable) -> Bool {
        if let otherStat = other as? Stats {
            return self.defense > otherStat.attack
        }
        // providing a good way if other does not conform to Stats
        return false
    }
}
0
votes

One way which work's for me is making a typealias in your Fightable protocol. So you can constraint the parameter of the fight function in your protocol extension. Due to this situation you also have to make your fight function generic (Fightable can than only be used as generic constraint).

In code it looks like this:

protocol Fightable {
    // make typealias
    typealias F = Fightable
    // make function generic
    func fight<F: Fightable>(other: F) -> Bool
}

protocol Stats {
    var defense: Int { get }
    var attack: Int { get }
}

// constraint F and Self
extension Fightable where F : Stats, Self: Stats {
    func fight(other: F) -> Bool {
        return self.defense > other.attack
    }
}

// example of an implementation
struct Human: Fightable {
    func fight<F: Fightable>(other: F) -> Bool {
        return true
    }
}