2016-12-23 6 views
8

私はEquatableプロトコルを実装している異なる構造体の配列を持っていて、それはコレクションを期待する関数に渡そうとしていますwhere T.Iterator.Element: Equatable。クラスを使用してclass Vehicle: Identifiable, Equatableを作成し、CarTractorVehicleに実装することで、この問題を解決する方法を知っています。しかし、これは構造体とプロトコルを使用することで可能かどうかを知りたいですか?あなたがしたいと - ここEquatableを実装している構造体の配列の操作

は私が

//: 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 

答えて

7

問題は、あなたが実際の種類として、自己または関連するタイプの要件にプロトコルを使用することはできません、エラーが言うように、あるが何をしようとしているの不自然な例ですこれらの要件がどのようなものであったかについてのタイプ情報を失う。この場合、==実装のパラメータの型情報が失われます。Equatableには、適合型と同じ型(つまり、Self)が指定されているとします。

ほとんどの場合、解決策はtype eraserです。 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プロパティの名前を変更していることに注意してください)

しかし、より一般的な解決策は、できるタイプの消しゴムで機能を保存することですタイプキャストの後にタイプ消去が作成された具体的なタイプと同じタイプであることを確認するために、==の実装に基づく2つの任意の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) 
+0

、ありがとうございました!私はそれが私のコードベースにどのように適合しているかを調べるためにそれを試してみるでしょうが、私はこれが問題の正しい解決策であると思います。乾杯! –

+1

@MarkoHlebarを助けて幸い:) – Hamish

関連する問題