【发布时间】:2019-12-08 21:35:15
【问题描述】:
我有一个带有关联类型的简单协议,以及一个返回此类型数组的协议扩展。
protocol Foo {
associatedtype Unit
}
extension Foo {
var allTheFoos: [Unit] {
return []
}
}
然后我有一个在计算属性中返回 some Foo 的结构,以及另一个返回 allTheFoos 数组的计算属性。
struct FakeFoo: Foo {
typealias Unit = Int
}
struct FooFactory {
var myFoo: some Foo {
return FakeFoo()
}
/* WHICH RETURN TYPE WILL
PLEASE THE SWIFT GODS?!
*/
var allTheFoos: [Foo.Unit] {
return myFoo.allTheFoos
}
}
allTheFoos 的返回类型与 Xcode 对 myFoo.allTheFoos 调用的自动完成类型建议相匹配,但可以理解的是,这会产生:
// var allTheFoos: [Foo.Unit] {}
ERROR: Associated type 'Unit' can only be used with a concrete type or generic parameter base
我的问题是:什么返回类型会让 Xcode 开心?
以下是我的尝试,以及相应的错误
// var allTheFoos: [some Foo.Unit] {}
ERROR: 'some' types are only implemented for the declared type of properties and subscripts and the return type of functions
// func allTheFoos() -> some [Foo.Unit]
ERROR: Associated type 'Unit' can only be used with a concrete type or generic parameter base
// func allTheFoos<U: Foo.Unit>() -> [U]
ERROR: Associated type 'Unit' can only be used with a concrete type or generic parameter base
ERROR: Cannot convert return expression of type '[(some Foo).Unit]' to return type '[U]'
// func allTheFoos<U>() -> [U] where U: (some Foo).Unit
ERROR: 'some' types are only implemented for the declared type of properties and subscripts and the return type of functions
仅供参考:我首先在计算属性中执行此操作的原因是为了在某些 SwiftUI 代码中保持简洁。
感谢您提供的任何帮助!
===========更新============
我在示例代码中遗漏了一些重要的内容,因此提供一些上下文:该代码用于单位转换应用程序,因此可以将摄氏度转换为开尔文,将千克转换为磅,以及将其他任何东西转换为其他任何东西。
protocol Unit: Equatable {
var suffix: String { get }
}
struct Value<UnitType: Unit> {
let amount: Double
let unit: UnitType
var description: String {
let formatted = String(format: "%.2f", amount)
return "\(formatted)\(unit.suffix)"
}
}
值被限制为单位类型,因此无法将摄氏度转换为升。
因此,我们有一个Conversion 协议将所有相似的单元存储在一起:
protocol Conversion {
associatedtype UnitType: Unit
var allUnits: [UnitType] { get }
func convert(value: Value<UnitType>, to unit: UnitType) -> Value<UnitType>
}
extension Conversion {
func allConversions(for value: Value<UnitType>) -> [Value<UnitType>] {
let units = self.allUnits.filter { $0 != value.unit }
return units.map { convert(value: value, to: $0) }
}
}
因此,温度转换的示例如下:
struct Temperature: Conversion {
enum Units: String, Unit, CaseIterable {
case celsius, farenheit, kelvin
var suffix: String {
switch self {
case .celsius: return "˚C"
case .farenheit: return "˚F"
case .kelvin: return "K"
}
}
}
var allUnits: [Units] { return Units.allCases }
func convert(value: Value<Units>, to unit: Units) -> Value<Units> {
/* ... */
}
}
最后,实际出现问题的应用代码在这里:
struct MyApp {
var current: some Conversion {
return Temperature()
}
// ERROR: Associated type 'UnitType' can only be used with a concrete type or generic parameter base
var allConversions: [Value<Conversion.UnitType>] {
// This value is grabbed from the UI
let amount = 100.0
let unit = current.allUnits.first!
let value = Value(amount: amount, unit: unit)
return current.allConversions(for: value)
}
}
【问题讨论】:
-
这是不可能的。虽然作品中的一些功能可能会在未来几年使部分实现成为可能(特别是可能与不透明返回类型一起使用的广义存在),但目前尚不清楚您真正想要在这里发生什么以及是否会发生可能的。调用代码是什么样的?我们可以从那里开始,设计一些可能不需要关联类型的东西(我认为你不是真的在这里)。
-
谢谢@RobNapier - 我现在意识到我试图简化问题并将其发布在这里,随后删除了很多关于我的意图的重要代码。马上更新。
标签: swift generics associated-types opaque-result-type