Обертки параметров Swift @autoclosure при условии явного закрытия

Рассмотрим следующую функцию:

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

Естественно, мы можем вызвать это так:

whatever(foo: 5)
// prints: 5

Однако предоставление явного аргумента замыкания заставляет компилятор жаловаться:

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

Это предназначено? Чтение документации для @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"

Теперь скажем, я хочу хешировать ключ 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>'
  }
}

Чтобы уточнить: defaultValue имеет тип () -> T, Предоставляя это init(key: String, defaultValue: () -> T), в моем ожидании должен просто работать, потому что аргумент и параметр имеют одинаковый тип (в то время как параметр @autoclosure).
Тем не менее, Swift, кажется, оборачивает предоставленное закрытие, эффективно создавая () -> () -> T, который создает Setting<() -> T> вместо Setting<T>,

Я могу обойти эту проблему, объявив init который принимает явно не @autoclosure параметр:

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

Что действительно пугает, так это то, что я могу просто переслать init принимая @autoclosure параметр, и это работает.

Я что-то здесь упускаю, или это просто невозможно в Swift предоставить аргументы закрытия @autoclosure параметры?

1 ответ

Решение

Swift ожидает, что вы передадите выражение, которое приводит к Int в whatever(foo:) и Swift обернет это выражение в замыкание типа () -> Int,

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

Когда вы называете это так:

func whatever(foo: {5})

Вы передаете выражение, которое приводит к () -> Int а не Int Свифт ожидает. Вот почему он предлагает вам добавить () и вызвать это замыкание, чтобы получить выражение, которое возвращает Int:

func whatever(foo: {5}())

Обратите внимание, что, поскольку Swift обертывания {5}() в закрытии, он не оценивается перед вызовом whatever(foo:) но на самом деле звонок задерживается, пока вы не оцените let x = foo(),

Вы можете проверить это, запустив это на игровой площадке:

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

whatever(foo: { print("hi"); return 3 }())

Выход:

inside whatever
hi
3
hi
3

Если ты хочешь 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 }())

Выход:

closure whatever
autoclosure whatever
two
6
autoclosure whatever
two
6
Другие вопросы по тегам