为什么必须将协议运算符实现为全局函数?

Why must a protocol operator be implemented as a global function?

我看到了这个 Swift Equatable Protocol 问题的答案,其中提到必须如何在全局范围内声明 == 方法。

如果我不采用 Equatable,我仍然可以声明 == 来测试我的两个类型之间的相等性。

// extension Foo: Equatable {}

func ==(lhs: Foo, rhs: Foo) -> Bool {
    return lhs.bar == rhs.bar
}

struct Foo {
    let bar:Int
}

它的实现需要在全局范围内声明,这使得它看起来 附带 并且 distinct 来自协议,即使 Equatable 被采纳了。

Equatable 协议不仅仅是语法糖,它只是让(我们和)编译器安全地知道我们的类型实现了协议所需的方法吗?

为什么必须全局声明运算符实现,即使是协议?这是因为调度操作员的方式不同吗?

来自documentation

的解释

The operator function is defined as a global function with a function name that matches the operator to be overloaded.

The function is defined globally, rather than as a method on the target class or structure, so that it can be used as an infix operator between existing instances of the target class or structure.

我用通用表达式 target class 或 structure

替换了引用中具体结构的名称 (Vector2D)

更新

来自 Xcode 8 beta 4 发行说明:

Operators can be defined within types or extensions thereof. For example:

struct Foo: Equatable {
    let value: Int
    static func ==(lhs: Foo, rhs: Foo) -> Bool {
        return lhs.value == rhs.value
    }
}

Such operators must be declared as static (or, within a class, class final), and have the same signature as their global counterparts. As part of this change, operator requirements declared in protocols must also be explicitly declared static:

protocol Equatable {
    static func ==(lhs: Self, rhs: Self) -> Bool
}

原创

This was discussed on the swift-evolution list recently (2016-01-31 through 2016-02-09 so far). 以下是 Chris Lattner 所说的关于在结构或 class 范围内声明运算符的内容:

Yep, this is a generally desirable feature (at least for symmetric operators). This would also be great to get dynamic dispatch of operators within class declarations. I don’t think we have a firm proposal nailing down how name lookup works with this though.

之后(回复 Haravikk):

What are the name lookup issues? Do you mean cases where an operator for Foo == Foo exists in more than one location?

是的。名称查找必须具有明确定义的搜索顺序,这 定义阴影和无效的多重定义规则。

Personally I’d just stick with what we have now, i.e- treat operator implementations within a specific class/struct as being globally defined anyway and throw an error if the same signature is declared more than once.

我们需要多个模块才能定义一个实例 运算符,我们需要扩展中的运算符,我们需要追溯 与任何其他成员一样遵守工作。

The fact that its implementation needs to be declared at a global scope, makes it seem incidental to and distinct from a protocol, even if Equatable was adopted.

每个协议都是如此,无论它需要全局函数、class 方法还是实例方法。您始终可以独立于是否存在需要它的协议来实现。

How is the Equatable protocol anything more than syntactic sugar that merely lets (us and) the compiler safely know that our type implemented the required method of the protocol?

那不是糖。这就是协议的定义和协议的全部要点。它告诉你这个类型有可用的这些东西可以应用到它。

Why does the operator implementation have to be globally declared, even for a protocol? Is this due to some different way that an operator is dispatched?

因为那是 Swift 语法。运算符函数实现必须是全局函数。 Swift 团队一直有兴趣对此进行更改以使其更加一致,但今天这就是 Swift 的工作方式。

在Swift标准库中,定义了'=='运算符。它对比较的一侧或另一侧没有关联性,并且在整个 Swift 运算符顺序中具有编号优先级。如果您在 'import Swift'

上用 CMD 单击 Swift,您可以检查一下
infix operator == {
    associativity none
    precedence 130
}

对于 Int,库中已经有 Int/Int8/Int16/Int32/Int64 作为 Equatable:

public func ==(lhs: Int, rhs: Int) -> Bool

与许多其他类型一样。

Int 在扩展中被设为 Hashable(为了符合该协议,您需要创建一个 var hashValue: Int { get },它会随着应用程序的每次执行而改变:

extension Int : Hashable {
    public var hashValue: Int { get }
}

运算符“==”和协议通常需要在顶层 struct/enum/class 之外声明,因为您正在扩展语言,目前,这就是 swift 的编译方式; Swift 的所有运算符都是在全局范围内定义的属性。

对于您的 'Foo',您通过添加函数采用了 Equatable:

func ==(lhs: Foo, rhs: Foo) -> Bool {
    return lhs.bar == rhs.bar
}

它使用协议中的 'Self' 局部变量:

public protocol Equatable {
    public func ==(lhs: Self, rhs: Self) -> Bool
}

之前有点发糖,因为需要手动做Equatable的实现。尽管 Rob Napier 提出了一个很好的观点,那就是协议的目的。

但那是 没有 的情况,即仅通过一致性,您就可以获得 Automated Synthesis 并且不再需要样板代码。

struct Country: Equatable {
  let name: String
  let capital: String
  var visited: Bool
}

就是这样! 编译器 将完成剩下的工作。

let france = Country(name: "France", capital: "Paris", visited: true)
let spain = Country(name: "Spain", capital: "Madrid", visited: true)
if france == spain { ... } // false

也就是说,如果您愿意,您仍然可以覆盖 == 函数的实现。

有关更多信息,请参阅 here