具有Void关联类型的通用Swift 4枚举

50

简述

在Swift 4中实例化一个泛型枚举成员,其关联值为Void,是否可能?

背景

我正在使用一个简单的Result枚举(类似于antitypical的Result库):

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

现在,我想用这个枚举来表示一个不产生实际结果值的操作的结果;操作要么是“成功”,要么是“失败”。为此,我将定义类型为Result<Void>,但我不知道如何创建Result实例,既不是let res: Result<Void> = .success,也不是let res: Result<Void> = .success()

你确定第二种方式可以吗?看起来好像可以工作 http://swift.sandbox.bluemix.net/#/repl/599d61b1b459cc41aee76d9d - pacification
有趣,感谢提供示例。我重新检查了一下,在Xcode 9 Beta 5中我得到了“调用中参数#1缺失”的错误提示。 - dr_barto
@Hamish:你可能是对的(我还停留在SE-0110和SE-0029... :) - Martin R
1
@Hamish:这也可能是SE-0029的结果:在Swift 3和4b5中,“构造函数”let f = Result<Void>.success的类型为(Void)->Result<Void>。在Swift 3中,您可以调用let r = f(),而在4b5中,您必须添加一个参数:let r = f(()) - Martin R
5个回答

82

在 Swift 3 中,您可以省略类型为 Void 的关联值:

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

在 Swift 4 中,您需要传递一个类型为 Void 的关联值:

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

15
有没有更好看的解决方案? - Rodrigo Ruiz
1
@RodrigoRuiz 你可以定义一个扩展来摆脱 (())请参见我的答案 - Hamish

54
在Swift 4中,带有Void关联值的枚举情况不再等同于带有空关联值列表的枚举情况。
我认为这是由于Martin所说的 SE-0029的结果,其中您不能再将参数元组传递给函数并将它们“扩展”到参数上(尽管该提案已在Swift 3中标记为已实现,但我认为这种特殊情况是在Swift 4的SE-0110实施中后来被解决的)。
因此,这意味着您不能再像Swift 4之前那样将(Void) -> T作为() -> T调用。现在必须显式地传递Void
let result = Result.success(())

然而,我觉得这样做相当丑陋,所以我通常会实现这样的扩展:

extension Result where Success == 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
(好的)我没有意识到你可以根据泛型的类型定义变量。 - GoldenJoe
太棒了!我从.success(Void())转移到了这个解决方案。 - Lensflare
Swift 5 版本将是 extension Result where Success == Void { ... },但由于某种原因,let r1 = Result<Void, Error>.success 编译出现 "Ambiguous use of 'success'" 错误,而 let r2: Result<Void, Error> = .success 则可以顺利编译。 - Martin R
@MartinR 真糟糕 - 问题在于 Result<Void, Error>.success 可以同时指代枚举的 .success 构造器和静态变量。已经有一个排名规则优先考虑变量而不是函数,但目前还没有考虑枚举构造器 :(. let r2: Result<Void, Error> = .success 可以工作,因为只有静态变量是类型为 Result<Void, Error> 的。之所以 Result.success 在我的原始示例中起作用是因为未指定通用占位符,因此只能解析为静态变量,因为它使用 Void 满足了占位符。 - Hamish
很不幸,我认为除了执行 let r2: Result<Void, Error> = .success 之外,目前没有一个好的解决方法。理想情况下,过载排名规则应该更像静态函数处理枚举实例构造器。更好的做法是,如果在AST中将枚举实例构造器表示为隐式生成的静态函数,则可以帮助消除与它们的其他不一致之处(但这可能无法在ABI稳定性状态下实现,因为我不确定编码是否相同)。 - Hamish

5

Void是一个空元组的简单类型别名: (),您可以将其用作以下任何一种:

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

4

我认为.success(Void())更具描述性和简洁。


4

Swift 5 更新了 Result,要求必须提供 Failure 参数,但仍然需要关联值:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接