协议中引用无效关联类型导致的神秘错误

Mysterious error caused by reference to invalid associated type in protocol

在线Swift5.1编译器编译如下代码报错。最重要的陈述如下:

main.swift:18:23: error: reference to invalid associated type 'Other' of type 'Sparrow'
func sing(with f: Other) {
                  ^

Whosebug 上的另一个线程提出了同样的问题,但情况更复杂。 (Reference to invalid associated type 'Iterator' of type 'DecodedArray<T>')。该主题还没有答案。

我的案例的代码和编译器报告的错误的完整列表如下所示:

    protocol Flier {
    associatedtype Other

    func flockTogether(with f:Other)
    func sing(with f:Other)
}

struct Sparrow : Flier {
    let name: String

    init (_ name: String) {
        self.name = name
    }

    func flockTogether(with f: Other) {
        print("Birds, \(name), and \(f.name), of a feather flock together.")
    }
    func sing(with f: Other) {
        sing()
        f.sing()
    }
    func sing () {
        print("Sparrow sings \"chirp, chirp\"!")
    }
}

struct Parrot {
    let name: String

    init (_ name: String) {
        self.name = name
    }

    func sing () {
        print("Parrot sings \"chuck, chuck\"!")
    }
}
struct Canary: Flier {
    let name: String
        
    init (_ name: String) {
        self.name = name
    }
    func flockTogether(with f: Other) {}
    func sing(with f: Other) {}

    func sing () {
        print("Canary sings \"tweet, tweet\"!")
    }
}
let sparrow = Sparrow("sparrow")
let parrot = Parrot("parrot")
let canary = Canary("canary")
sparrow.flockTogether(with: parrot)
sparrow.sing(with: parrot)
sparrow.flockTogether(with: canary)
sparrow.sing(with: canary)

编译器报错:

main.swift:18:23: error: reference to invalid associated type 'Other' of type 'Sparrow'
    func sing(with f: Other) {
                      ^
main.swift:8:8: error: type 'Sparrow' does not conform to protocol 'Flier'
struct Sparrow : Flier {
       ^
main.swift:2:20: note: protocol requires nested type 'Other'; do you want to add it?
    associatedtype Other
                   ^
main.swift:38:8: error: type 'Canary' does not conform to protocol 'Flier'
struct Canary: Flier {
       ^
main.swift:2:20: note: protocol requires nested type 'Other'; do you want to add it?
    associatedtype Other
                   ^
compiler exit status 1

请帮我看看上面的代码哪里出了问题。非常感谢!

这段代码有很多问题。

  1. SparrowCanary 被声明为符合 Flier 但没有说明它们各自的 Other 类型是什么。

  2. 您试图将 parrotcanary 都传递给 sparrow.flockTogether(with:)sparrow.sing(with:),但这些方法只接受一种类型的对象 - Sparrow.Other。这一点以及以上一点表明您可能误解了关联类型是什么。我建议你 read about them.

  3. 您正在尝试访问不一定存在的内容,例如 f.namef.sing()。回想一下 f 是一个 Other,它不受任何类型的限制,因此它可以是 anything。而“任何东西”并不总是有一个 name 供您访问。

我建议以下方法使呼叫者正常工作:

  1. 删除关联类型并改用泛型方法。如果调用者要决定是传入 Parrot 还是 Canary.

    ,则关联类型是不合适的
  2. namesing() 添加到 Flier 以便编译器知道任何符合 Flier 的东西都有这些成员。如果我们再将 Other(上述泛型方法的泛型参数)约束为 Flier,那么我们可以毫无问题地访问 sing()name

  3. 也使Parrot符合Flier

固定代码现在看起来像:

protocol Flier {
    var name: String { get }
    func flockTogether<Other: Flier>(with f:Other)
    func sing<Other: Flier>(with f:Other)
    func sing()
}

struct Sparrow : Flier {
    let name: String

    init (_ name: String) {
        self.name = name
    }

    func flockTogether<Other: Flier>(with f:Other) {
        print("Birds, \(name), and \(f.name), of a feather flock together.")
    }
    func sing<Other: Flier>(with f:Other) {
        sing()
        f.sing()
    }
    func sing () {
        print("Sparrow sings \"chirp, chirp\"!")
    }
}

struct Parrot : Flier{
    func flockTogether<Other>(with f: Other) where Other : Flier { }
    
    func sing<Other>(with f: Other) where Other : Flier { }
    
    let name: String

    init (_ name: String) {
        self.name = name
    }

    func sing () {
        print("Parrot sings \"chuck, chuck\"!")
    }
}
struct Canary: Flier {
    let name: String

    init (_ name: String) {
        self.name = name
    }
    func flockTogether<Other: Flier>(with f:Other) {}
    func sing<Other: Flier>(with f:Other) {}

    func sing () {
        print("Canary sings \"tweet, tweet\"!")
    }
}