I always thought that something like var foo: MyClass
meant that foo
is of MyClass type.
I think this is what’s causing your difficulties with this concept. Task is not really a type, and task is not a variable of type Task. When someone says “task is of type Task” in reference to a protocol, it’s really just shorthand for what I said earlier, that task is a variable that will be of a type conforming to the Task protocol.
Here’s something you might find interesting. There’s a Swift function type(of:) that returns the type of a variable. Add that to the for loop in the example code @ibex10 posted above:
for _ in 0..<3 {
let task : Task = getTask ()
print("task is a \(type(of:task))")
task.run ()
}
and you get something like
task is a BarTask
BarTask the Magnificent: starting...
BarTask the Magnificent: working...
BarTask the Magnificent: finished.
task is a FooTask
FooTask: starting...
FooTask: working...
FooTask: finished.
task is a BarTask
BarTask the Magnificent: starting...
BarTask the Magnificent: working...
BarTask the Magnificent: finished.
But, if we create a new variable similar to task, don’t assign it a value, and try to print its type:
var task2: Task
print("\(type(of:task2))")
we get an error:
error: MyPlayground.playground:97:18: error: variable 'task2' used before being initialized
Variables that are defined as adhering to a protocol do not have an inherent type. They cannot have a type until they are assigned a value, at which point they will temporarily adopt the type of whatever value they are assigned:
var task2: Task = BarTask()
print("\(type(of:task2))")
task2 = FooTask()
print("\(type(of:task2))")
produces
BarTask
FooTask
And the error message is not due solely to type(of:) being given an uninitialized variable. If you define a variable to be of a type, you can get its type before it is initialized:
var a: Int
print("\(type(of:a))")
prints
Int
but if you try to print the value of a you’ll get an error that it is uninitialized. You can do that because a has a defined type independent of its value; task and task2 do not, because they are only defined as adhering to a protocol.
Edit: It looks like the error message doesn’t mean what I thought it did. When I tried the following:
var a: FooTask
print("\(type(of:a))")
I got the same uninitialized variable error that I did with task2 even though FooTask is a type. It looks like maybe the reason it works for an uninitialized Int is due to Int being a value type rather than a reference type, which suggests that variables defined as adhering to a protocol are implemented as reference types.
I think the rest still applies, though. You can see the type of task and task2 changing as their values change, and at no point does type(of:) say that either of them are of type Task.
Edit 2: And reading further into the documentation for type(of:), it’s apparently possible for that function to return a protocol name under certain circumstances.
And reading even further, there’s this, from swift.org:
Protocols don’t actually implement any functionality themselves. Nonetheless, you can use protocols as a fully fledged types in your code. Using a protocol as a type is sometimes called an existential type , which comes from the phrase “there exists a type T such that T conforms to the protocol”.
and
Because protocols are types, begin their names with a capital letter (such as FullyNamed
and RandomNumberGenerator
) to match the names of other types in Swift (such as Int
, String
, and Double
).
So there it is, all official: Swift protocols are types.