为什么实例方法在Swift中有这种类型



给定此代码:

struct Foo {
func f() {}
}
let f = Foo.f // (Foo) -> () -> ()

为什么f的类型是(Foo) -> () -> ()而不是(Foo) -> ()?例如,像Foo.f这样的方法可以直接与(Foo) -> …类型的自由函数互换,这难道没有意义吗?

为什么f的类型是(Foo) -> () -> ()而不是(Foo) -> ()

这就是当前未应用的实例方法引用的实现方式;它们是遵循"给我一个实例,我会给你一个部分应用的实例方法"(部分应用于该实例)模型的curried函数。

然而,这在某些领域是有问题的,首先是因为它们的形式(Self, Args...) -> Ret通常更有用,但更重要的是,因为这会导致mutating方法的问题。在当前系统中,这些最终看起来像(inout Self) -> (Args...) -> Ret,这是有问题的,因为inout的突变窗口只持续呼叫的持续时间。

这意味着以下内容当前正在编译,但实际上是未定义的行为:

struct S {
var i: Int
mutating func increment() {
i += 1
}
}
var s = S(i: 0)
let unappliedIncrement = S.increment
let increment = unappliedIncrement(&s)
increment() // undefined behaviour

这些是SE-0042背后的主要动机,它将把未应用的实例方法引用从(Self) -> (Args...) -> Ret的形式更改为
(Self, Args...) -> Ret的形式(对于inoutSelf将是inout Self——允许在没有UB的情况下更改实例)。

这个提议还没有实现,但一旦实现,假设空的参数列表被压平(这样你就不会得到一个尾随的Void参数),Foo.f的类型确实是
(Foo) -> Void

这是因为,这么说:

let f = Foo.f

您已经将f描述为方法Foo.f。但是在Foo的声明中,f是一个实例方法。因此,您意外地发现了一个深层次的秘密,即实例方法是伪装的类方法;您看到的是secret类方法的签名。

如果你说

let f = Foo().f

你会得到预期的结果。

最新更新