Reputation: 2485
I'm reading the Sequence documentation and in an example that they use (see below) the requirements for Sequence are inferred. My question is how is it inferred. I understand how inferencing works in simpler cases but in this example I cannot understand how Swift infers things.
I see that Sequence's makeIterator method has a default implementation but I don't understand how the return value is inferred here.
Example
struct Countdown: Sequence, IteratorProtocol {
var count: Int
mutating func next() -> Int? {
if count == 0 {
return nil
} else {
defer { count -= 1 }
return count
}
}
}
Reference for types mentioned above
IteratorProtocol {
func next() -> Self.Element?
associatedtype Iterator
associatedtype Element
}
Sequence {
func makeIterator() -> Self.Iterator
associatedtype Iterator
associatedtype Element
}
Upvotes: 1
Views: 199
Reputation: 271810
Let's start with the implementation of IteratorProtocol.next
. The compiler sees this implementation:
mutating func next() -> Int? {
if count == 0 {
return nil
} else {
defer { count -= 1 }
return count
}
}
And notices that it returns an Int?
. Well, IteratorProtocol.next
is supposed to return a Self.Element?
, so it infers that IteratorProtocol.Element == Int
. Now Coundown
satisfies IteratorProtocol
.
Note that Sequence
and IteratorProtocol
share the associated type Element
. Once Swift figures out the witness for IteratorProtcol.Element
, it's as if you declared a new type alias Element
in Countdown
, and it just so happens that Sequence
requires that Countdown.Element
to exist.
After that, the compiler infers Iterator == Self
. This is so that the default implementation of makeIterator
is available. However, it is quite a mystery how the compiler can infer this, because with only these information, the type can't normally be inferred, as can be shown by creating your own sequence and iterator protocols.
protocol MyIterator {
associatedtype Element
mutating func next() -> Element?
}
protocol MySequence {
associatedtype Element where Element == Iterator.Element
associatedtype Iterator : MyIterator
func makeIterator() -> Iterator
}
extension MySequence where Self == Self.Iterator {
func makeIterator() -> Iterator {
return self
}
}
struct Countdown: MySequence, MyIterator { // doesn't compile
var count: Int
mutating func next() -> Int? {
if count == 0 {
return nil
} else {
defer { count -= 1 }
return count
}
}
}
After looking into the source code, I suspect there might be some compiler magic going on, especially here:
// Provides a default associated type witness for Iterator when the
// Self type is both a Sequence and an Iterator.
extension Sequence where Self: IteratorProtocol {
// @_implements(Sequence, Iterator)
public typealias _Default_Iterator = Self
}
This seems to set a "preferred" type for Iterator
to be inferred as. It seems to be saying "When Iterator
can't be inferred to be anything, try Self
". I can't find _Default_Iterator
anywhere else, which is why I concluded it's compiler magic. The whole purpose of this is to allow you to conform to Sequence
by only conforming to IteratorProtocol
and implementing next
, as the documentation said you can do.
Now that Iterator == Self
, we have also satisfied the constraint on Element
:
associatedtype Element where Self.Element == Self.Iterator.Element
Thus we have shown that Countdown
conforms to Sequence
.
Upvotes: 1