【问题标题】:How do you provide a default argument for a generic function with type constraints?如何为具有类型约束的泛型函数提供默认参数?
【发布时间】:2019-03-30 13:36:34
【问题描述】:

以下函数定义是合法的 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 的限制,还是我心智模型的限制?

【问题讨论】:

  • 我不知道你能做到这一点;谢谢你的提问!

标签: swift string compiler-errors


【解决方案1】:

重要的约束是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”。

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

【讨论】:

  • @LeoDabus 我不明白这将如何解决默认参数情况。您将如何实现语法 doSomething() 作为 RangeReplaceableCollection 的扩展?
  • 我认为重载方法是我正在寻找的。值得一提的是,我的实际用例有点复杂,实际上并不涉及字符串,但这里的讨论仍然非常有见地!
猜你喜欢
  • 2018-02-18
  • 2010-11-09
  • 2020-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-24
相关资源
最近更新 更多