给定此代码:
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
的形式(对于inout
,Self
将是inout Self
——允许在没有UB的情况下更改实例)。
这个提议还没有实现,但一旦实现,假设空的参数列表被压平(这样你就不会得到一个尾随的Void
参数),Foo.f
的类型确实是(Foo) -> Void
。
这是因为,这么说:
let f = Foo.f
您已经将f
描述为类方法Foo.f
。但是在Foo的声明中,f
是一个实例方法。因此,您意外地发现了一个深层次的秘密,即实例方法是伪装的类方法;您看到的是secret类方法的签名。
如果你说
let f = Foo().f
你会得到预期的结果。