I'm trying to write a protocol that conforms to the Collection Protocol, and it has an associatedType - Object and a property object.
protocol DDCDataSource: Collection
{
associatedtype Object
var object: Object {get set}
}
I want to add some default functionality for the case where Object also conforms to the Collection protocol, namely just directly return Object's implementation of these required Collection properties and functions. It seems like it all works except for Collection's requirement for a subscript.
Cannot subscript a value of type 'Self.Object' with an index of type 'Self.Object.Index'
extension DDCDataSource where Object: Collection
{
typealias Index = Object.Index
var startIndex: Object.Index {
get {
return object.startIndex
}
}
var endIndex: Object.Index {
get {
return object.endIndex
}
}
subscript(position: Object.Index) -> Element
{
return object[position]
}
func index(after i: Object.Index) -> Object.Index {
return object.index(after: i)
}
}
Short answer: Change the return type of the subscript method
to Object.Element
subscript(position: Object.Index) -> Object.Element {
return object[position]
}
or add a type alias (in a similar way as you did for the Index
type)
typealias Element = Object.Element
subscript(position: Object.Index) -> Element {
return object[position]
}
That makes the code compile and run as expected.
Explanation: The subscript
method of Collection
is declared as
subscript(position: Self.Index) -> Self.Element { get }
where Self.Index
and Self.Element
are associated types
of `Collection. With your code
subscript(position: Object.Index) -> Element {
return object[position]
}
the compiler infers Self.Index
to be Object.Index
, but there
is no relation between Self.Element
and Object.Element
(which is
returned by object[position]
). The error becomes more apparent
if you add an explicit cast:
subscript(position: Object.Index) -> Element {
return object[position] as Element
}
Now the compiler complains
error: 'Self.Object.Element' is not convertible to 'Self.Element'; did you mean to use 'as!' to force downcast?
The correct solution is not the forced cast but to make the compiler
know that Self.Element
is Object.Element
, by adding a type alias
or by changing the return type
subscript(position: Object.Index) -> Object.Element {
return object[position]
}
so that the compiler infers DDCDataSource.Element
to be Object.Element
.
Full self-contained example: (Swift 4, Xcode 9 beta 6)
(Note that you can omit the get
keyword for read-only computed
properties.)
protocol DDCDataSource: Collection {
associatedtype Object
var object: Object { get set }
}
extension DDCDataSource where Object: Collection {
var startIndex: Object.Index {
return object.startIndex
}
var endIndex: Object.Index {
return object.endIndex
}
subscript(position: Object.Index) -> Object.Element {
return object[position]
}
func index(after i: Object.Index) -> Object.Index {
return object.index(after: i)
}
}
struct MyDataSource: DDCDataSource {
var object = [1, 2, 3]
}
let mds = MyDataSource()
print(mds[1]) // 2
for x in mds { print(x) } // 1 2 3