Generic Swift 4 enum с ассоциированным с Void типом

TL;DR

Возможно ли создать экземпляр элемента перечисления Swift 4 с ассоциированным значением типа Void?

Фон

Я использую простое перечисление результатов (похожее на antitypical Result):

enum Result<T> {
  case success(T)
  case error(Error?)
}

Теперь я хотел бы использовать это перечисление для представления результата операции, которая не дает фактического значения результата; операция либо преуспела, либо не удалась. Для этого я бы определил тип как Result<Void>, но я борюсь с тем, как создать экземпляр Result, ни let res: Result<Void> = .success, ни let res: Result<Void> = .success() не работает.

Ответ 1

В Swift 3 вы можете опустить связанное значение типа Void:

let res: Result<Void> = .success()

В Swift 4 вам необходимо передать связанное значение типа Void:

let res: Result<Void> = .success(())
// Or just:
let res = Result.success(())

Ответ 2

В Swift 4 событие enum со связанным значением Void больше не эквивалентно случаю enum с пустым списком связанных значений.

Я считаю, что это как говорит Мартин, результат SE-0029, где вы больше не можете передавать кортеж аргументов функции и иметь "splat" по всем параметрам (хотя предложение было отмечено в Swift 3, я считаю, что этот конкретный случай был взят позже в реализации SE-0110 для Swift 4).

В результате это означает, что вы больше не можете называть (Void) -> T как () -> T в Swift 4. Теперь вам нужно передать Void в явном виде:

let result = Result.success(())

Однако я нахожу это довольно уродливым, поэтому я обычно реализую расширение следующим образом:

extension Result where T == Void {
    static var success: Result {
        return .success(())
    }
}

Что позволяет говорить такие вещи:

var result = Result.success
result = .success

Стоит отметить, что это обходное решение не ограничивается только перечисляемыми случаями, оно также может использоваться с методами в целом. Например:

struct Foo<T> {
  func bar(_ a: T) {}
}

extension Foo where T == Void {
  func bar() { bar(()) }
}

let f = Foo<Void>()

// without extension:
f.bar(())

// with extension:
f.bar()

Ответ 3

Void - это простые типы для пустого кортежа:(), поэтому вы можете использовать его как любое из следующих:

let res1: Result<Void> = .success(())
let res2 = Result<Void>.success(())
let res3 = Result.success(() as Void)
let res4 = Result.success(())