Swift @autoclosure 参数包装提供的显式闭包

Cab*_*bus 4 closures swift

考虑以下函数:

func whatever(foo: @autoclosure () -> Int) {
  let x = foo()
  print(x)
}
Run Code Online (Sandbox Code Playgroud)

当然,我们可以这样调用它:

whatever(foo: 5)
// prints: 5
Run Code Online (Sandbox Code Playgroud)

然而,提供显式的闭包参数会导致编译器抱怨:

whatever(foo: { 5 })
// Error: Function produces expected type 'Int'; did you mean to call it with '()'?
Run Code Online (Sandbox Code Playgroud)

这是有意的吗?阅读文档时,@autoclosure我没有找到关于参数是否始终被包装的声明,即使在提供闭包时也是如此。我的理解@autoclosure是:
采用闭包论证。如果参数不是闭包,但与闭包返回的类型相同,则将其包装。
然而,我看到的行为是:无论如何都要结束争论。

一个更详细的例子让我觉得这很奇怪:

struct Defaults {

  static var dispatcher: Defaults = ...

  subscript<T>(setting: Setting<T>) -> T { ... }

  struct Setting<T> {
    let key: String
    let defaultValue: () -> T

    init(key: String, defaultValue: @escaping @autoclosure () -> T) {
      self.key = key
      self.defaultValue = defaultValue
    }
  }
}

extension Defaults.Setting {
  static var nickname: Defaults.Setting<String> {
    return Defaults.Setting(key: "__nickname", defaultValue: "Angela Merkel")
  }
}

//  Usage:
Defaults.dispatcher[.nickname] = "Emmanuel Macron"
Run Code Online (Sandbox Code Playgroud)

现在假设我想对一个Setting值的键进行哈希处理:

extension Defaults.Setting {
  var withHashedKey: Defaults.Setting<T> {
    return Defaults.Setting(key: key.md5(), defaultValue: defaultValue)
    // Error: Cannot convert return expression of type 'Defaults.Setting<() -> T>' to return type 'Defaults.Setting<T>'
  }
}
Run Code Online (Sandbox Code Playgroud)

澄清一下:defaultValue是类型() -> T。在我的期望中,将其提供给init(key: String, defaultValue: () -> T)应该可以工作,因为参数和参数具有相同的类型(而参数是@autoclosure)。
然而,Swift 似乎包装了提供的闭包,有效地创建了() -> () -> T,它创建了Setting<() -> T>而不是Setting<T>. 我可以通过声明 an which 采用显式非参数

来解决此问题:init@autoclosure

extension Defaults.Setting {
  init(key: String, defaultValue: @escaping () -> T) {
    self.init(key: key, defaultValue: defaultValue)
  }
}
Run Code Online (Sandbox Code Playgroud)

真正令人畏惧的是,我可以简单地转发到init获取@autoclosure参数并且它可以工作。

我在这里遗漏了一些东西,还是 Swift 中的设计不可能为@autoclosure参数提供闭包参数?

vac*_*ama 6

Swift 希望您传递一个生成Intto的表达式whatever(foo:),并且 Swift 会将该表达式包装在类型为 的闭包中() -> Int

func whatever(foo: @autoclosure () -> Int) {
    let x = foo()
    print(x)
}
Run Code Online (Sandbox Code Playgroud)

当你这样称呼它时:

func whatever(foo: {5})
Run Code Online (Sandbox Code Playgroud)

你传递的表达式() -> Int不是IntSwift 期望的结果。这就是为什么它建议您添加()并调用该闭包以获取返回以下值的表达式Int

func whatever(foo: {5}())
Run Code Online (Sandbox Code Playgroud)

请注意,由于 Swift 包装{5}()在一个闭包中,因此在调用 之前不会对它进行求值whatever(foo:),但实际上调用会被延迟,直到您对 求值let x = foo()

您可以通过在 Playground 中运行来验证这一点:

func whatever(foo: @autoclosure () -> Int) {
    print("inside whatever")
    let x = foo()
    print(x)
    let y = foo()
    print(y)
}

whatever(foo: { print("hi"); return 3 }())
Run Code Online (Sandbox Code Playgroud)

输出:

inside whatever
hi
3
hi
3
Run Code Online (Sandbox Code Playgroud)

如果您whatever(foo:还希望能够采用() -> Int闭包,请重载它并在调用后调用自动闭包版本foo

func whatever(foo: @autoclosure () -> Int) {
    print("autoclosure whatever")
    let x = foo()
    print(x)
}

func whatever(foo: () -> Int) {
    print("closure whatever")
    whatever(foo: foo())

}

whatever(foo: { print("two"); return 6 })
whatever(foo: { print("two"); return 6 }())
Run Code Online (Sandbox Code Playgroud)

输出:

closure whatever
autoclosure whatever
two
6
autoclosure whatever
two
6
Run Code Online (Sandbox Code Playgroud)