[英]Operation on an array of structs implementing Equatable
我有一組不同的結構,都實現了Equatable
協議,我試圖將它傳遞給一個需要where T.Iterator.Element: Equatable
集合的函數。 我知道如何通過使用類來解決這個問題,只需創建一個class Vehicle: Identifiable, Equatable
,然后使Car
和Tractor
實現Vehicle
。 但是,我想知道使用結構和協議是否可行?
這是我正在嘗試做的一個人為的例子
//: Playground - noun: a place where people can play
protocol Identifiable {
var ID: String { get set }
init(ID: String)
init()
}
extension Identifiable {
init(ID: String) {
self.init()
self.ID = ID
}
}
typealias Vehicle = Identifiable & Equatable
struct Car: Vehicle {
var ID: String
init() {
ID = ""
}
public static func ==(lhs: Car, rhs: Car) -> Bool {
return lhs.ID == rhs.ID
}
}
struct Tractor: Vehicle {
var ID: String
init() {
ID = ""
}
public static func ==(lhs: Tractor, rhs: Tractor) -> Bool {
return lhs.ID == rhs.ID
}
}
class Operator {
func operationOnCollectionOfEquatables<T: Collection>(array: T) where T.Iterator.Element: Equatable {
}
}
var array = [Vehicle]() //Protocol 'Equatable' can only be used as a generic constraint because Self or associated type requirements
array.append(Car(ID:"VW"))
array.append(Car(ID:"Porsche"))
array.append(Tractor(ID:"John Deere"))
array.append(Tractor(ID:"Steyr"))
var op = Operator()
op.operationOnCollectionOfEquatables(array: array) //Generic parameter 'T' could not be inferred
問題是,正如錯誤所說,您不能將具有Self或相關類型要求的協議用作實際類型 - 因為您丟失了這些要求的類型信息。 在這種情況下,您將丟失==
實現的參數的類型信息 - 正如Equatable
所說,它們必須與符合類型(即Self
)的類型相同。
解決方案幾乎總是構建一個類型橡皮擦 。 如果期望類型相等,如果它們的id
屬性相等,這可以像存儲id
屬性並在==
實現中進行比較一樣簡單。
struct AnyVehicle : Equatable {
static func ==(lhs: AnyVehicle, rhs: AnyVehicle) -> Bool {
return lhs.id == rhs.id
}
let id : String
init<T : Vehicle>(_ base: T) {
id = base.id
}
}
(注意我將您的ID
屬性重命名為id
以符合Swift命名約定)
但是,更通用的解決方案是在類型擦除器中存儲一個函數,它可以在類型轉換后根據它們的 ==
實現比較兩個任意的符合Vehicle
實例,以確保它們與類型橡皮擦的具體類型相同是用。創建的。
struct AnyVehicle : Equatable {
static func ==(lhs: AnyVehicle, rhs: AnyVehicle) -> Bool {
// forward to both lhs's and rhs's _isEqual in order to determine equality.
// the reason that both must be called is to preserve symmetry for when a
// superclass is being compared with a subclass.
// if you know you're always working with value types, you can omit one of them.
return lhs._isEqual(rhs) || rhs._isEqual(lhs)
}
let base: Identifiable
private let _isEqual: (_ to: AnyVehicle) -> Bool
init<T : Vehicle>(_ base: T) {
self.base = base
_isEqual = {
// attempt to cast the passed instance to the concrete type that
// AnyVehicle was initialised with, returning the result of that
// type's == implementation, or false otherwise.
if let other = $0.base as? T {
return base == other
} else {
return false
}
}
}
}
print(AnyVehicle(Car(id: "foo")) == AnyVehicle(Tractor(id: "foo"))) // false
print(AnyVehicle(Car(id: "foo")) == AnyVehicle(Car(id: "bar"))) // false
print(AnyVehicle(Car(id: "foo")) == AnyVehicle(Car(id: "foo"))) // true
var array = [AnyVehicle]()
array.append(AnyVehicle(Car(id: "VW")))
array.append(AnyVehicle(Car(id: "Porsche")))
array.append(AnyVehicle(Tractor(id: "John Deere")))
array.append(AnyVehicle(Tractor(id: "Steyr")))
var op = Operator()
// compiles fine as AnyVehicle conforms to Equatable.
op.operationOnCollectionOfEquatables(array: array)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.