Swift 键入擦除尝试:"Reference to invalid associated type"
Swift Type Erasure attempt: "Reference to invalid associated type"
我正在做一个伪造的练习来尝试实现类型擦除的容器。
import Foundation
protocol MoverType {
func move()
}
extension MoverType {
func move() { print("\(type(of: self)) is moving") }
}
class Slithering: MoverType {}
class Walking: MoverType {}
class Trotting: MoverType {}
protocol Animal {
associatedtype Mover: MoverType
var mover: Mover { get }
}
class Snake: Animal {
let mover = Slithering()
}
class Dog: Animal {
let mover = Trotting()
}
class Human: Animal {
let mover = Walking()
}
class AnyAnimal: Animal { // ERROR: Type 'AnyAnimal' does not conform to protocol 'Animal'
var _mover: () -> Mover
init<A: Animal>(animal: A) where Mover == A.Mover {
_mover = {
return animal.mover
}
}
// ERROR HERE: "Reference to invalid associated type "Mover" of of type "AnyAnimal"
var mover: Mover { fatalError() }
}
let d = AnyAnimal(animal: Dog())
let s = AnyAnimal(animal: Snake())
let animals = [d, s] // Array<AnyAnimal>
for a in animals {
a.mover.move()
}
我故意不希望我的 AnyAnimal
容器成为 AnyAnimal<T>
容器。因为,我希望能够在 Array<AnyAnimal>
.
中存储许多 Animal
个实例
但是,正如您在上面的代码中看到的,编译器抱怨 AnyAnimal
class。据我了解,Mover
的协议要求将由 AnyAnimal
初始值设定项中的通用 where
子句解决。
请帮助我了解缺少的内容。 (或者,是否有可能首先创建一个非通用类型擦除包装器?)
您的代码无法编译,因为需要在编译时通过为 Mover
协议提供具体实现来解析关联类型。
你可以做的是同时删除 MoverType
协议:
struct AnyMover: MoverType {
private let mover: MoverType
init(_ mover: MoverType) {
self.mover = mover
}
func move() {
mover.move()
}
}
class AnyAnimal: Animal {
let mover: AnyMover
init<A: Animal>(animal: A) {
mover = AnyMover(animal.mover)
}
}
我同意 Cristik 关于添加 AnyMover 类型橡皮擦的观点。这就是按照字面上的意思去做的方法。但如果你走这条路,你通常会错误地设计你的协议。例如,虽然我知道这是捏造的,但它是错误设计协议的一个很好的例子。 Animal 协议几乎可以肯定是:
protocol Animal {
func move()
}
然后您的 associatedtype
和相关问题就烟消云散了。而且您的界面更有意义。动物可以移动。这不是什么 "with a Mover." 我希望这种类型的每次使用看起来都与您的示例几乎相同:animal.mover.move()
。也就是说 mover
是调用者不应该关心的实现细节。
在 [Animal]
的循环中,您还能 做什么?您如何编写使用 .mover
而 没有 调用 .move
的通用代码?没有任何其他方法。
我知道这是捏造的,但这种情况在很多真实案例中都是真实存在的,大家要提高警惕。当您发现自己伸手去拿打字橡皮时,尤其是当您开始伸手去拿两层打字橡皮时,您需要问问自己是否做错了什么。并非总是如此,但大多数时候,你面前的问题都有更好的解决方案。
顺便说一句,这里的另一种方法是保留 Mover,但只是让它们成为协议。来电者真的想知道爬行者和滑行者之间的区别吗?隐藏这不是整个目标吗?如果是这样,你可以这样走:
protocol Animal {
var mover: MoverType { get }
}
再一次,所有的问题都烟消云散了。
无论哪种方式,如果有 mover
可用,您仍然可以自动实现 move()
方法。例如,您可以这样设计协议:
// Something that moves animals
protocol Mover {
func move(animal: Animal)
}
// Something that has a mover
protocol MoverProviding {
var mover: Mover { get }
}
// And of course Animals. They might be MoverProviding. They might not.
protocol Animal {
func move()
}
// But if they *are* MoverProviding, we can use that.
extension Animal where Self: MoverProviding {
func move() {
mover.move(animal: self)
}
}
当您键入 associatedtype
时,您通常会想 "this protocol is all about adding extra algorithms (methods and functions) to other types." 如果协议的重点是允许异构集合,那么您可能走错了路。类型橡皮擦有时很有用且很重要,但如果您觉得自己非常需要它们(并且 尤其是 如果您因为异构集合而需要它们),那么您可能遇到了设计问题。
我正在做一个伪造的练习来尝试实现类型擦除的容器。
import Foundation
protocol MoverType {
func move()
}
extension MoverType {
func move() { print("\(type(of: self)) is moving") }
}
class Slithering: MoverType {}
class Walking: MoverType {}
class Trotting: MoverType {}
protocol Animal {
associatedtype Mover: MoverType
var mover: Mover { get }
}
class Snake: Animal {
let mover = Slithering()
}
class Dog: Animal {
let mover = Trotting()
}
class Human: Animal {
let mover = Walking()
}
class AnyAnimal: Animal { // ERROR: Type 'AnyAnimal' does not conform to protocol 'Animal'
var _mover: () -> Mover
init<A: Animal>(animal: A) where Mover == A.Mover {
_mover = {
return animal.mover
}
}
// ERROR HERE: "Reference to invalid associated type "Mover" of of type "AnyAnimal"
var mover: Mover { fatalError() }
}
let d = AnyAnimal(animal: Dog())
let s = AnyAnimal(animal: Snake())
let animals = [d, s] // Array<AnyAnimal>
for a in animals {
a.mover.move()
}
我故意不希望我的 AnyAnimal
容器成为 AnyAnimal<T>
容器。因为,我希望能够在 Array<AnyAnimal>
.
Animal
个实例
但是,正如您在上面的代码中看到的,编译器抱怨 AnyAnimal
class。据我了解,Mover
的协议要求将由 AnyAnimal
初始值设定项中的通用 where
子句解决。
请帮助我了解缺少的内容。 (或者,是否有可能首先创建一个非通用类型擦除包装器?)
您的代码无法编译,因为需要在编译时通过为 Mover
协议提供具体实现来解析关联类型。
你可以做的是同时删除 MoverType
协议:
struct AnyMover: MoverType {
private let mover: MoverType
init(_ mover: MoverType) {
self.mover = mover
}
func move() {
mover.move()
}
}
class AnyAnimal: Animal {
let mover: AnyMover
init<A: Animal>(animal: A) {
mover = AnyMover(animal.mover)
}
}
我同意 Cristik 关于添加 AnyMover 类型橡皮擦的观点。这就是按照字面上的意思去做的方法。但如果你走这条路,你通常会错误地设计你的协议。例如,虽然我知道这是捏造的,但它是错误设计协议的一个很好的例子。 Animal 协议几乎可以肯定是:
protocol Animal {
func move()
}
然后您的 associatedtype
和相关问题就烟消云散了。而且您的界面更有意义。动物可以移动。这不是什么 "with a Mover." 我希望这种类型的每次使用看起来都与您的示例几乎相同:animal.mover.move()
。也就是说 mover
是调用者不应该关心的实现细节。
在 [Animal]
的循环中,您还能 做什么?您如何编写使用 .mover
而 没有 调用 .move
的通用代码?没有任何其他方法。
我知道这是捏造的,但这种情况在很多真实案例中都是真实存在的,大家要提高警惕。当您发现自己伸手去拿打字橡皮时,尤其是当您开始伸手去拿两层打字橡皮时,您需要问问自己是否做错了什么。并非总是如此,但大多数时候,你面前的问题都有更好的解决方案。
顺便说一句,这里的另一种方法是保留 Mover,但只是让它们成为协议。来电者真的想知道爬行者和滑行者之间的区别吗?隐藏这不是整个目标吗?如果是这样,你可以这样走:
protocol Animal {
var mover: MoverType { get }
}
再一次,所有的问题都烟消云散了。
无论哪种方式,如果有 mover
可用,您仍然可以自动实现 move()
方法。例如,您可以这样设计协议:
// Something that moves animals
protocol Mover {
func move(animal: Animal)
}
// Something that has a mover
protocol MoverProviding {
var mover: Mover { get }
}
// And of course Animals. They might be MoverProviding. They might not.
protocol Animal {
func move()
}
// But if they *are* MoverProviding, we can use that.
extension Animal where Self: MoverProviding {
func move() {
mover.move(animal: self)
}
}
当您键入 associatedtype
时,您通常会想 "this protocol is all about adding extra algorithms (methods and functions) to other types." 如果协议的重点是允许异构集合,那么您可能走错了路。类型橡皮擦有时很有用且很重要,但如果您觉得自己非常需要它们(并且 尤其是 如果您因为异构集合而需要它们),那么您可能遇到了设计问题。