以下函数定义是合法的 Swift:
func doSomething<T: StringProtocol>(value: T = "abc") {
// ...
}
编译器能够确定默认参数 "abc"
是一个 String
,并且 String
符合 StringProtocol
.
但是这段代码无法编译:
func doSomething<T: Collection>(value: T = "abc") where T.Element == Character {
// ...
}
编译错误:
Default argument value of type 'String' cannot be converted to type 'T'
似乎编译器拥有与第一种情况一样多的信息来确定 String
确实可以转换为 T
。此外,如果我删除默认参数并使用相同的值调用该函数,它会起作用:
doSomething(value: "abc")
能否以不同的方式编写此函数,以便我可以提供默认的 String
参数?这是 Swift 的局限性,还是我心智模型的局限性?
最佳答案
重要的约束是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”。
通常,默认参数只是重载的便利,因此您通常可以用缺少该参数的显式重载替换任何默认参数。
关于swift - 如何为具有类型约束的泛型函数提供默认参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55431977/