如何为具有类型约束的泛型函数提供默认参数?

时间:2019-03-30 13:36:34

标签: swift string compiler-errors

以下功能定义为合法Swift:

func doSomething<T: StringProtocol>(value: T = "abc") {
    // ...
}

编译器能够确定默认参数"abc"String,并且String符合StringProtocol

但是此代码无法编译:

func doSomething<T: Collection>(value: T = "abc") where T.Element == Character {
    // ...
}

编译器错误:

  

类型'String'的默认参数值不能转换为类型'T'

似乎编译器将拥有与第一种情况一样多的信息来确定String确实可转换为T。此外,如果我删除默认参数并以相同的值调用该函数,则它会起作用:

doSomething(value: "abc")

此函数的编写方式可以不同,以便我可以提供默认的String参数吗?这是Swift的局限性,还是我的思维模式的局限性?

1 个答案:

答案 0 :(得分:2)

主要约束是T: ExpressibleByStringLiteral。这就是允许从字符串文字中初始化某些内容的原因。

func doSomething<T: Collection>(value: T = "abc")
    where T.Element == Character, T: ExpressibleByStringLiteral {
    // ...
}

正如Leo Dabus指出的那样,T.Element == Character在技术上不是必需的,但是删除它会改变含义。仅仅因为某物是一个集合并且可以用字符串文字初始化,并不意味着它的元素就是字符。

还值得注意的是,尽管所有这些都是可能的,但通常它是较差的Swift IMO。 Swift没有任何方法来表示默认的 type 是什么,因此doSomething()在所有这些情况下都会导致“无法推断出通用参数'T'”。

正确的解决方案IMO是过载,它避免了所有这些问题:

func doSomething<T: StringProtocol>(value: T) {
}

func doSomething() {
    doSomething(value: "abc")
}

这使您不仅可以使默认参数成为“可以用文字"abc"初始化的东西”,而且还可以使您真正地知道:默认值是字符串“ abc”。

通常,默认参数只是重载的便利,因此您通常可以将任何默认参数替换为缺少该参数的显式重载。