Swift:自从引入主要关联类型以来,是否仍然存在类型擦除的用例?

Jan*_*Jan 5 generics type-systems protocols swift

Swift 5.7 引入了主要关联类型。在尝试此功能时,我想知道是否仍然存在类型擦除类型(例如 AnySequence)的用例,或者主要关联类型是否会使这些类型完全过时?

\n

例如,如果我们有以下代码:

\n
protocol Car<Fuel> {\n    associatedtype Fuel\n    func drive(fuel: Fuel)\n}\n\nstruct Electricity {}\nstruct Petrol {}\n\nstruct SportsCar: Car {\n    func drive(fuel: Petrol) { print("\xef\xb8\x8f") }\n}\n\nstruct FamilyCar: Car {\n    func drive(fuel: Electricity) { print("") }\n}\n\nstruct WorkCar: Car {\n    func drive(fuel: Electricity) { print("") }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

我们现在可以制作一个仅包含电动汽车的数组:

\n
let electricCars: [any Car<Electricity>] = [FamilyCar(), WorkCar()]\n
Run Code Online (Sandbox Code Playgroud)\n

以前我会写这样的东西:

\n
struct AnyCar<Fuel>: Car {\n   //Implementation\n}\n\nlet electricCars: = [AnyCar(FamilyCar()), AnyCar(WorkCar())]\n
Run Code Online (Sandbox Code Playgroud)\n

是否仍然存在自定义结构“AnyCar”有意义的情况?

\n

谢谢你!

\n

Ita*_*ber 5

尽管主要关联类型确实有助于消除使用某些存在类型的许多边缘,但仍然存在使用具体类型进行手动类型擦除的用例Any\xe2\x80\xa6

\n

存在类型动态地调度其接口声明的方法直至底层值,但关键的是,它们本身不能:

\n
    \n
  1. 符合协议
  2. \n
  3. 实施方法
  4. \n
  5. 满足静态类型要求
  6. \n
\n

一个非常常见的例子是Equatable一致性。我们可以更新Car协议以采用Equatable一致性,以表明Cars 应该能够相等:

\n
protocol Car<Fuel>: Equatable {\n    associatedtype Fuel\n    func drive(fuel: Fuel)\n}\n\nstruct SportsCar: Car { \xe2\x80\xa6 }\nstruct FamilyCar: Car { \xe2\x80\xa6 }\nstruct WorkCar: Car { \xe2\x80\xa6 }\n
Run Code Online (Sandbox Code Playgroud)\n

Car但是,虽然如果知道两个值的静态类型,则可以检查两个值是否相等,但无法检查两个any Car值是否相等:

\n
WorkCar() == WorkCar() // \xe2\x9c\x85 true\n\nlet electricCars: [any Car<Electricity>] = [WorkCar(), WorkCar()]\nelectricCars[0] == electricCars[1]\n//  Type \'any Car<Electricity>\' cannot conform to \'Equatable\'\n//    Only concrete types such as structs, enums, and classes can conform to protocols\n//    Required by referencing operator function \'==\' on \'Equatable\' where \'Self\' = \'any Car<Electricity>\'\n
Run Code Online (Sandbox Code Playgroud)\n

Equatable有无法满足Self的要求any CarAnyCar但是,如果您编写自己的类型,则可以这样做:

\n
struct AnyCar<Fuel>: Car {\n    private let inner: any Car<Fuel>\n    private let isEqual: (AnyCar<Fuel>) -> Bool\n\n    // The key to the `Equatable` conformance is capturing the _static_ type\n    // of the value we\'re wrapping.\n    init<C: Car<Fuel>>(_ car: C) {\n        inner = car\n        isEqual = { anyCar in\n            guard let otherCar = anyCar.inner as? C else {\n                return false\n            }\n\n            return car == otherCar\n        }\n    }\n\n    func drive(fuel: Fuel) {\n        inner.drive(fuel: fuel)\n    }\n\n    static func ==(_ lhs: Self, _ rhs: Self) -> Bool {\n        lhs.isEqual(rhs)\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

使用这个包装器,您可以检查两个任意AnyCar值是否相等:

\n
let electricCars: [AnyCar<Electricity>] = [AnyCar(FamilyCar()), AnyCar(WorkCar()), AnyCar(WorkCar())]\nelectricCars[0] == electricCars[1] // \xe2\x9c\x85 false\nelectricCars[1] == electricCars[2] // \xe2\x9c\x85 true\n
Run Code Online (Sandbox Code Playgroud)\n

这种方法对于您来说可能看起来很熟悉,AnyHashable因为它用作可以包含任何类型键的字典的通用键类型。您无法使用以下方法实现相同的功能any Hashable

\n
let d: [any Hashable: Any] = ["hi" : "there"] //  Type \'any Hashable\' cannot conform to \'Hashable\'\n
Run Code Online (Sandbox Code Playgroud)\n

与 , 相反AnyCarAnyHashable它的好处是如此普遍和必要,编译器会自动将类型包装起来,AnyHashable这样您就不需要自己做这件事,从而使其基本上不可见。

\n