For example we have two simple protocols:
protocol Container {
var items: [Item] {get}
}
protocol Item {
var name: String {get}
}
And we want to use a function that requires Item to be Equatable, like:
extension Container {
func indexOfItem(_ item: Item) -> Int? {
items.firstIndex(of: item)
}
}
Yet we can't write it like this, as firstIndex requires argument conforming to Equatable. But Item being protocol, can't conform to Equatable.
I've tried to implement it using type erasure, but it became too complicated.
So I am curious is there some workaround to implement such functions (provided that actual items are equatable)?
CodePudding user response:
Since you're using func firstIndex
of an array in this func indexOfItem(_ item: Item) -> Int?
therefore the Item
has to be a concrete object (behind the scene of firstIndex
func is comparing each element of an array and print out the index of the element).
There are 2 ways to do this
- First is using associatedtype to keep your protocol generic
protocol Item: Equatable {
var name: String { get }
}
protocol Container {
associatedtype Item
var items: [Item] { get }
}
struct MyItem: Item {
var name: String
}
extension Container where Item == MyItem {
func indexOfItem(_ item: Item) -> Int? {
return items.firstIndex(of: item)
}
}
- Second is using an equatable object
MyItem
instead a protocolItem
inside theContainer
protocol
protocol Item {
var name: String { get }
}
protocol Container {
var items: [MyItem] { get }
}
struct MyItem: Item, Equatable {
var name: String
}
extension Container {
func findIndex(of item: MyItem) -> Int? {
return items.firstIndex(of: item)
}
}
CodePudding user response:
Finally find simple enough solution:
То make protocol generic with associated type and constraint this type to Equatable.
public protocol Container {
associatedtype EquatableItem: Item, Equatable
var items: [EquatableItem] {get}
}
public protocol Item {
var name: String {get}
}
public extension Container {
func indexOfItem(_ item: EquatableItem) -> Int? {
items.firstIndex(of: item)
}
}
This compiles and now if I have some types
struct SomeContainer {
var items: [SomeItem]
}
struct SomeItem: Item, Equatable {
var name: String
}
I only need to resolve associatedtype to provide protocol conformance for SomeContainer type:
extension SomeContainer: Container {
typealias EquatableItem = SomeItem
}