Swift:自从引入主关联类型以来,是否还有类型擦除的用例?

t2a7ltrp  于 2023-06-21  发布在  Swift
关注(0)|答案(3)|浏览(111)

Swift 5.7引入了主关联类型。在试验这个特性时,我想知道是否仍然有像AnySequence这样的类型擦除类型的用例,或者主关联类型是否使这些类型完全过时?
例如,如果我们有以下代码:

protocol Car<Fuel> {
    associatedtype Fuel
    func drive(fuel: Fuel)
}

struct Electricity {}
struct Petrol {}

struct SportsCar: Car {
    func drive(fuel: Petrol) { print("🏎️") }
}

struct FamilyCar: Car {
    func drive(fuel: Electricity) { print("🚗") }
}

struct WorkCar: Car {
    func drive(fuel: Electricity) { print("🚙") }
}

我们现在可以只使用电动汽车来制作一个数组:

let electricCars: [any Car<Electricity>] = [FamilyCar(), WorkCar()]

以前我会写这样的东西:

struct AnyCar<Fuel>: Car {
   //Implementation
}

let electricCars: = [AnyCar(FamilyCar()), AnyCar(WorkCar())]

是否仍有自定义结构“AnyCar”有意义的情况?
谢谢你!

aij0ehis

aij0ehis1#

虽然主关联类型确实有助于消除使用某些存在类型的许多边缘,但仍然存在使用具体Any…类型进行手动类型擦除的用例。
存在类型动态地将它们的接口声明的方法分派到底层值,但关键的是,它们自己不能:
1.符合方案
1.实施方法
1.满足静态类型要求
一个非常常见的例子是Equatable一致性。我们可以更新Car协议以采用Equatable一致性,以指示Car s应该能够被等同:

protocol Car<Fuel>: Equatable {
    associatedtype Fuel
    func drive(fuel: Fuel)
}

struct SportsCar: Car { … }
struct FamilyCar: Car { … }
struct WorkCar: Car { … }

但是,如果知道两个Carstatic 类型,则可以检查它们是否相等,但不能检查两个any Car值是否相等:

WorkCar() == WorkCar() // ✅ true

let electricCars: [any Car<Electricity>] = [WorkCar(), WorkCar()]
electricCars[0] == electricCars[1]
// 🛑 Type 'any Car<Electricity>' cannot conform to 'Equatable'
//    Only concrete types such as structs, enums, and classes can conform to protocols
//    Required by referencing operator function '==' on 'Equatable' where 'Self' = 'any Car<Electricity>'

Equatable具有Self的要求,而any Car无法满足;但是,如果您编写自己AnyCar类型,则可以做到这一点:

struct AnyCar<Fuel>: Car {
    private let inner: any Car<Fuel>
    private let isEqual: (AnyCar<Fuel>) -> Bool

    // The key to the `Equatable` conformance is capturing the _static_ type
    // of the value we're wrapping.
    init<C: Car<Fuel>>(_ car: C) {
        inner = car
        isEqual = { anyCar in
            guard let otherCar = anyCar.inner as? C else {
                return false
            }

            return car == otherCar
        }
    }

    func drive(fuel: Fuel) {
        inner.drive(fuel: fuel)
    }

    static func ==(_ lhs: Self, _ rhs: Self) -> Bool {
        lhs.isEqual(rhs)
    }
}

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

let electricCars: [AnyCar<Electricity>] = [AnyCar(FamilyCar()), AnyCar(WorkCar()), AnyCar(WorkCar())]
electricCars[0] == electricCars[1] // ✅ false
electricCars[1] == electricCars[2] // ✅ true

这种方法在使用AnyHashable作为字典的通用键类型时可能看起来很熟悉,字典可以包含任何类型的键。你不能用any Hashable实现同样的功能:

let d: [any Hashable: Any] = ["hi" : "there"] // 🛑 Type 'any Hashable' cannot conform to 'Hashable'

AnyCar相反,AnyHashable的好处是如此普遍和必要,以至于编译器会自动 Package AnyHashable中的类型,这样你就不需要自己做了,使它在很大程度上不可见。

hgb9j2n6

hgb9j2n62#

存在(any)类型不符合它们的协议,所以any Car<Electric>本身不是Car。
考虑一个只能容纳特定类型汽车的车库:

struct Garage<GarageCar: Car> {
    init(car: GarageCar) {}
}

创建一个Garage<FamilyCar>是合法的:

let familyCarGarage = Garage(car: FamilyCar())

但是创建Garage<any Car>是不合法的:

let anyCar: any Car = FamilyCar()
let anyCarGarage = Garage(car: anyCar) // Type 'any Car' cannot conform to 'Car'

但是,在这种情况下,您可以构建AnyCar来处理它。Garage<AnyCar>有效,即使Garage<any Car>无效。

struct AnyCar<Fuel>: Car {
    func drive(fuel: Fuel) {}
    init(_ car: some Car<Fuel>) {}
}

let explicitAnyCar = AnyCar(FamilyCar())
let anyCarGarage = Garage(car: explicitAnyCar)

这种情况不应该经常出现,通常应该尽可能避免使用any(尽可能首选some)。any类型现在比早期版本的Swift中的强大得多,你应该更喜欢它们而不是显式类型擦除。但是,仍然有角落的情况下,他们失败了。
在Swift的未来版本中,any类型可能会符合某些协议(特别是那些没有staticinit要求的协议,这是主要的症结所在)。但我们还没到那一步。有关未来发展方向的详细信息,请参阅:

(And各种各样的东西从他们内部联系起来。里面有很多东西)。

7xzttuei

7xzttuei3#

问题是类型擦除是一个概念,这意味着我们不应该将对象的确切实现细节传递给用户。
这个概念是通过创建AnyCar自定义类型来使用的。
但是随着any关键字的引入,我们仍然使用相同的概念,只是方式不同。
如果代码使用any <Protocol>变得“太复杂”,我们仍然可以使用自定义结构类型

相关问题