在Swift中,从技术角度来看,为什么编译器关心协议是否只能用作通用约束?

在Swift中,从技术角度来看,为什么编译器关心协议是否只能用作通用约束?,swift,Swift,在Swift中,从技术角度来看,为什么编译器关心协议是否只能用作通用约束 假设我有: protocol Fooable { associated type Bar: Equatable func foo(bar: Bar) { bar==bar } } 为什么我以后不能声明一个接受可食对象作为参数的func 在我看来,编译器应该只关心一个给定的Fooable可以被发送一个带有参数“bar”的消息“foo”,该参数是一个equalable,因此响应消息“=

在Swift中,从技术角度来看,为什么编译器关心协议是否只能用作通用约束

假设我有:

protocol Fooable {
    associated type Bar: Equatable
    func foo(bar: Bar) {
        bar==bar
    }
}
为什么我以后不能声明一个接受可食对象作为参数的func

在我看来,编译器应该只关心一个给定的Fooable可以被发送一个带有参数“bar”的消息“foo”,该参数是一个equalable,因此响应消息“==”

我理解Swift是静态类型的,但为什么Swift在这种情况下还要真正关心类型,因为唯一重要的是给定的消息是否可以有效地发送到对象


我试图理解这背后的原因,因为我怀疑一定有一个很好的理由。

在上面的例子中,如果您编写的函数使用了
Fooable
参数,例如

func doSomething(with fooable:Fooable) {
    fooable.foo(bar: ???) // what type is allowed to be passed here? It's not _any_ Equatable, it's the associated type Bar, which here would be...???
}
什么类型可以传递到
fooable.foo(条:)
?它不能是任何
相等的
,它必须是特定的关联类型

最终,它将问题归结为引用“Self”或具有关联类型的协议最终具有不同的接口,具体实现基于这些接口(即Self的特定类型或特定关联类型)。因此,这些协议可以被视为缺少关于直接解决它们所需的类型和签名的信息,但仍然可以作为一致性类型的模板,因此可以用作通用约束

例如,编译器将接受这样编写的函数:

func doSomething<T: Fooable>(with fooable:T, bar: T.Bar) {
    fooable.foo(bar: bar)
}
既然
a
b
是相等的,我们可以比较它们是否相等,对吗?但事实上,,
a
的相等实现仅限于将字符串与字符串进行比较,
b
的相等实现仅限于将Int与Int进行比较。因此,最好像其他泛型一样考虑泛型协议,以认识到
equalable
equalable
不是同一协议,即使它们是相同的两者都被认为是“平等的”

  • 至于为什么你可以有一个
    [AnyHashable:Any]
    类型的字典,而不是
    [Hashable:Any]
    ,这一点变得越来越清楚了。哈希协议继承自Equatable,因此它是一个“通用协议”。这意味着对于任何可散列类型,必须有一个
    func==(左:Self,右:Self)->Bool
    。字典使用hashValue和相等比较来存储和检索键。但是,即使字符串键和Int键都符合Hashable/Equatable,字典如何比较它们的相等性呢?不可能。因此,您需要将密钥包装在一个名为AnyHashable的特殊“类型橡皮擦”中。类型橡皮擦的工作原理对于这个问题的范围来说太详细了,但只需说像AnyHashable这样的类型橡皮擦使用某种类型T:Hashable进行实例化,然后将对hashValue的请求转发到它的包装类型,并实现
    ==(左:AnyHashable,右:AnyHashable)->Bool
    ,也使用包装类型的相等实现。我认为这个要点应该能很好地说明如何实现“anyequalable”类型的橡皮擦

    向前看,因为AnyHashable是一个单一的具体类型(不像Hashable协议那样是泛型类型),所以可以使用它来定义字典。因为AnyHashable的每个实例都可以包装不同的Hashable类型(String、Int等),还可以生成一个hashValue,并检查是否与任何其他AnyHashable实例相等,这正是字典的键所需要的

    因此,从某种意义上说,像AnyHashable这样的类型擦除器是一种实现技巧,它可以将通用协议转换为普通协议。通过删除/丢弃与泛型关联的类型信息,但保留所需的方法,可以有效地将Hashable的特定一致性抽象为通用类型“AnyHashable”,该类型可以封装任何可哈希的内容,但可以在非泛型环境中使用

  • 如果您回顾一下创建“anyequalable”实现的要点,然后回头看看如何从前面的代码中转换出不可能/非编译的代码,那么这些可能都会结合在一起:

    var a: Equatable = "A String"
    var b: Equatable = 100
    let equal = a == b
    
    在这个概念上相似但实际上有效的代码中:

    var a: AnyEquatable = AnyEquatable("A String")
    var b: AnyEquatable = AnyEquatable(100)
    let equal = a == b  
    

  • 此处允许传递的是什么类型?它不是可平衡的,它是相关的类型栏,这里是…?
    好吧,我如何声明它,使类型是可平衡的,使响应消息的任何类型,
    ==
    都可以传入?编译器不允许我只拥有一个函数,
    func-foo(bar:equalable)
    。我认为协议的要点是定义消息传递需求,因此我可以保证一个可食的东西总是能够响应
    foo(bar:someequalable())
    。为什么编译器关心equalable类型的任何特定内容?为什么我可以有一个
    [AnyHashable:Any]
    类型的字典,而不是
    [Hashable:Any]
    。为什么编译器使用其中一个比另一个更快乐?我只是想理解。@CommaToast用更多的细节扩展了答案,希望能在评论中回答您的问题。我仍然认为Swift在这种情况下不自动键入erase是非常愚蠢的,相反,为了让协议像协议一样工作,迫使用户跳出疯狂的圈套。几乎和他们一年来专注于字符串一样愚蠢,但仍然无法为我们设计一种在字符串上使用标准数字下标的方法。Arrgh-lololCompare;简而言之,没有真正的理由说明具有相关类型/
    Self
    需求的协议不能作为实际类型使用。
    var a: AnyEquatable = AnyEquatable("A String")
    var b: AnyEquatable = AnyEquatable(100)
    let equal = a == b