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

Mysterious error caused by reference to invalid associated type in protocol

提问人:Michael May 提问时间:10/17/2020 更新时间:10/17/2020 访问量:839

问:

在线 Swift 5.1 编译器编译以下代码并报告错误。最重要的一个如下:

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

Stackoverflow 上的另一个线程提出了一个更复杂的问题。(引用类型为“DecodedArray<T>”的无效关联类型“Iterator”)。目前还没有关于该线程的答案。

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

    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

请帮助我找出上面代码中出了什么问题。谢谢!

Swift 协议 关联类型

评论

2赞 Leo Dabus 10/17/2020
鹦鹉不应该遵守 Flier 协议吗?Flier 协议不应该需要 name 属性吗?Flier 不应该需要一个名为 sing 的通用方法 (Flier) 吗?为什么需要关联类型?尝试protocol Flier { var name: String { get } func flockTogether<Other: Flier>(with f: Other) func sing<Other: Flier>(with f: Other) func sing() }

答:

3赞 Sweeper 10/17/2020 #1

这段代码有很多问题。

  1. Sparrow并被声明为符合,但没有说明它们各自的类型是什么。CanaryFlierOther

  2. 您正在尝试将 a 和 a 传递给 和 ,但这些方法只接受一种类型的对象 - 。这一点以及上面的一点表明,您可能误解了什么是关联类型。我建议你阅读它们parrotcanarysparrow.flockTogether(with:)sparrow.sing(with:)Sparrow.Other

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

我建议采取以下措施使调用方工作:

  1. 删除关联的类型并改用泛型方法。如果调用方决定是否传入 或 .ParrotCanary

  2. 添加 and,以便编译器知道任何符合的内容都具有这些成员。如果我们随后将(上述泛型方法的泛型参数)限制为 ,那么我们可以毫无问题地访问。namesing()FlierFlierOtherFliersing()name

  3. 也符合ParrotFlier

修复后的代码现在如下所示:

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\"!")
    }
}