2

I have a protocol that looks like this:

protocol MyProtocol {
   associatedtype SpeedType
   var name: String {get set}
   func forward(_: SpeedType)
}

I made 2 simple classes that conform to this protocol:

class A: MyProtocol {
   typealias SpeedType = Double
   var name: String

   init(name:String) {
       self.name = name
   }

   func forward(_ s: Double) {
       print("Moving \(s) km/h")
   }
}

class B: MyProtocol {
   typealias SpeedType = Int
   var name: String

   init(name:String) {
       self.name = name
   }

   func forward(_ s: Int) {
       print("Moving \(s) km/h")
   }
}

What I want to achieve is to be able to declare a variable of type MyProtocol, and initialize it later like so:

let x: Bool = true
var person: MyProtocol
if x {
   person = A(name: "Robot")
} else {
   person = B(name: "Human")
}

Before I made forward() method "generic" I was able to do this, however now I am getting the next error: Protocol "MyProtocol" can only be used as generic constraint because it has Self or associated type requirement.

So my goal is to have a method forward() that can take as an argument parameter of a type that I specify, and also be able to declare a variable of a type that conforms to my protocol.

Hamish
  • 78,605
  • 19
  • 187
  • 280
Alexei
  • 511
  • 1
  • 10
  • 22
  • 1
    Possible duplicate of [Protocol can only be used as a generic constraint because it has Self or associatedType requirements](http://stackoverflow.com/questions/36348061/protocol-can-only-be-used-as-a-generic-constraint-because-it-has-self-or-associa) – Scott Thompson May 06 '17 at 03:29

1 Answers1

5

Swift doesn't allow this.

Here's why: you don't know anything about the type of argument person.forward(_:) takes. There is no way to call it. MyProtocol essentially defines an open-ended set of independent types.

If you don't want to be able to call person.forward(_:), and you just want to be able to access the non-generic person.name property, then split your protocol into a base, non-generic protocol defining name, and a sub-protocol that adds the generic forward(_:) method.

protocol NamedThing {
    var name: String {get set}
}

protocol MovableNamedThing: NamedThing {
    associatedtype SpeedType
    func forward(_: SpeedType)
}

class A: MovableNamedThing {
    typealias SpeedType = Double
    var name: String

    init(name:String) {
        self.name = name
    }

    func forward(_ s: Double) {
        print("Moving \(s) km/h")
    }
}

class B: MovableNamedThing {
    typealias SpeedType = Int
    var name: String

    init(name:String) {
        self.name = name
    }

    func forward(_ s: Int) {
        print("Moving \(s) km/h")
    }
}

let x: Bool = true
var person: NamedThing
if x {
    person = A(name: "Robot")
} else {
    person = B(name: "Human")
}
rob mayoff
  • 375,296
  • 67
  • 796
  • 848