问题描述
编译器不会编译以下代码,因为它断然声明:
协议类型
Fooesque
无法符合Fooesque
,因为只有具体类型才能符合协议。
protocol Fooesque { var bar: Int { get set } }
class FooStruct: Fooesque { var bar = 42 }
struct Bar<F: Fooesque> {
typealias MyFoo = F
var foo: MyFoo
init(foo: MyFoo) { self.foo = foo }
}
struct Whee {
var someSpecificFoo: Fooesque
func process() { let bar = Bar(foo: someSpecificFoo) } // error line
}
var whee = Whee(someSpecificFoo: FooStruct())
显然这是一个错误,因为someSpecificFoo
已经被声明为符合Fooesque
的具体实例(这就是var someSpecificFoo: Fooesque
的意思)。 >
如果我们尝试通过将其更改为var someSpecificFoo: some Fooesque
来进行修复,那么越野车编译器会说:
属性声明了不透明的返回类型,但是没有可用于从其推断基础类型的初始化器表达式
现在,它不仅无法像正常情况一样自动合成一个初始化程序表达式,而且即使我们手动添加一个初始化程序表达式也会失败。添加:
init<F: Fooesque>(foo: F) { self.foo = foo }
无法将类型
F
的值分配为类型some Fooesque
...即使F符合Fooesque!太破碎了!
有人找到这种混乱的解决方法吗?
到目前为止,我发现的唯一解决方法是简单地将@objc
添加到第一行和import Foundation
,在这种情况下,它可以很好地工作。
解决方法
我不是Swift编译专家,但我不认为这是真的。
显然这是一个错误,因为someSpecificFoo已被声明为符合Fooesque的具体实例(这就是var someSpecificFoo:Fooesque的意思)。
似乎问题出在那。该声明没有具体内容。可以是符合Fooesque的任何内容。当创建带有F
作为协议Fooesque
的Bar时,是说Fooesque
应该符合Fooesque
的规定,而不能这样做。只有具体类型可以做到这一点。
如果将行更改为var someSpecificFoo: FooStruct
,则它似乎可以工作。但这也许不是您要的。
不知道您实际上要做什么,因为这些类型都是如此构成的,但是也许有一个更具体的示例会有所帮助?
干杯!
,它不是错误的编译器;而是您对泛型约束的误解。
Bar
是一种通用类型,可以具体化为符合Fooesque
的特定类型。
例如,您可以使用Bar<FooStruct>
类型。此具体类型具有以下init声明:
init(foo: FooStruct)
Bar<FooStruct>
不仅仅接受任何符合Fooesque
的类型-它仅接受FooStruct
。
它明显是否 init(foo: Fooesque)
,但是您可以通过在概念上执行以下操作来期望它的行为:
let f: Fooesque = ...
let bar = Bar(foo: f)
您期望上方的Bar的具体类型是什么?
编译器根据参数的类型推断F
为Fooesque
,因此它尝试创建具体的类型Bar<Fooesque>
,但失败了,因为Fooesque
-作为协议-不符合其他协议,包括自身。
但是您可能会说,不,我不希望泛型类型为Fooesque
;我想要一些符合Fooesque
的类型。
好,哪一个?假设在上面的示例中为FooStruct
:
let f: Fooesque = ....
let bar = Bar<FooStruct>(foo: f) // ERROR
再次出现错误,因为Bar<FooStruct>
期望以FooStruct
作为参数,而不是任何Fooesque
。
类似地,如果您有AnotherFooStruct: Fooesque
,那么您将拥有Bar<AnotherFooStruct>
类型,并期望以AnotherFooStruct
作为参数。
因此-类似地-如果您有任何(通用)F
类型-在您的示例中别名为MyFoo
-那么Bar<MyFoo>
仅接受MyFoo
类型-不是Fooesque
。
MyFoo
的类型是在编译时确定的,它基于传入参数的静态类型来显式或隐式确定。