Swift泛型:不能将类型的值转换为预期的参数类型 [英] Swift Generics: Cannot convert value of type to expected argument type
问题描述
这是我的代码:
protocol SomeProtocol {
}
class A :SomeProtocol {
}
func f1< T:SomeProtocol>(ofType:T.Type,listener:(T?) - > Void){
}
)b $ b func f2< T:SomeProtocol>(ofType:T.Type,listener:([T]?) - > Void){
}
func g {
let l1:(SomeProtocol?) - > Void = ...
let l2:([SomeProtocol]?) - > Void = ...
f1(ofType:A.self,listener:l1)//无错误
f2(ofType:A.self,listener:l2)//编译错误:无法将键入'([SomeProtocol]?) - >无效'到预期参数类型'([_]?) - >无效'
}
第二个闭包有一个参数<强类型>泛型类型对象的数组?
Swift 4.1 Update
这是一个在此拉取请求中修复的错误,其中将使其成为Swift 4.1的发布版本。您的代码现在可以在4.1快照中按预期编译。
Pre Swift 4.1
这看起来好像只是将编译器拉得太远了。 它可以处理来自将子类型元素数组添加到超类型元素数组中,例如 它可以处理具有超类型参数的函数转换为具有子类型参数的函数,例如
[A]
至 [SomeProtocol]
- this是协变性的。值得注意的是,数组一直是一个边缘案例,因为任意泛型是不变的。某些集合,如 Array
,只需从编译器获得特殊待遇允许协变。
(SomeProtocol) - > Void
到(A) - > Void
- 这是逆变。
然而,它似乎目前无法在一个去(但它真的应该能够;随时提交bug )。
对于它的价值,这与泛型没有任何关系,下面重现相同的行为:
protocol SomeProtocol {} $ b $ class A:SomeProtocol {}
func f1(listener:(A) - > Void){}
func f2(listener: ([A]) - > Void){}
func f3(listener:() - > [SomeProtocol]){}
func g(){
let l1:(SomeProtocol) - > Void = {_in}
f1(listener:l1)// NO ERROR
let l2:([SomeProtocol]) - > Void = {_in}
f2(listener:l2)
//编译器错误:无法转换'([SomeProtocol]) - >无效'到
//预期参数类型'([A]) - > Void'
//对于函数返回类型,它是相同的故事
let l3:() - > [A] = {[]}
f3(listener:l3)
//编译器错误:无法转换类型'() - > [A]'到
//期望的参数类型'() - > [SomeProtocol]'
}
在这种情况下,一个闭包表达式充当两个函数类型之间的蹦床:
//转换a([SomeProtocol]) - >无效为([A]) - >虚空。
//编译器推断闭包表达式的类型为([A]) - >无效,并且在
//实现中,$ 0隐式地从[A]转换为[SomeProtocol]。
f2(listener:{l2($ 0)})
//转换a() - > [A]到a() - > [SomeProtocol]。
//编译器推断闭包表达式为type() - > [SomeProtocol]和
//实现中,l3的结果从[A]隐式转换为[SomeProtocol]
f3(listener:{l3()})
并且,应用于您的代码:
f2(ofType:A.self,listener:{l2($ 0)})
这是有效的,因为编译器推断闭包表达式的类型是([T]?) - > Void
,它可以传递给 f2
。在实现闭包的过程中,编译器将 $ 0
从 [T]?
隐式转换为 [SomeProtocol]?
。
func f2< T:SomeProtocol>(ofType type:T.Type,listener:([SomeProtocol]?) - > Void){
//传递a类型([T]?)的闭包表达式 - >无效到原来的f2,我们然后
//处理从[T]的转换?到[SomeProtocol]?在关闭。
//(以及我们,我的意思是编译器,隐式地)
f2(ofType:type,listener:{(arr:[T]?)in listener(arr)})
$
允许您再次将它称为 f2(ofType: A.self,listener:l2)
。
Here is my code:
protocol SomeProtocol {
}
class A: SomeProtocol {
}
func f1<T: SomeProtocol>(ofType: T.Type, listener: (T?) -> Void) {
}
func f2<T: SomeProtocol>(ofType: T.Type, listener: ([T]?) -> Void) {
}
func g() {
let l1: (SomeProtocol?) -> Void = ...
let l2: ([SomeProtocol]?) -> Void = ...
f1(ofType: A.self, listener: l1) // NO ERROR
f2(ofType: A.self, listener: l2) // COMPILE ERROR: Cannot convert value of type '([SomeProtocol]?) -> Void' to expected argument type '([_]?) -> Void'
}
What is the problem with the second closure having an argument of an array of generic type objects?
解决方案 Swift 4.1 Update
This is a bug that was fixed in this pull request, which will make it into the release of Swift 4.1. Your code now compiles as expected in a 4.1 snapshot.
Pre Swift 4.1
This just looks like you're just stretching the compiler too far.
It can deal with conversions from arrays of sub-typed elements to arrays of super-typed elements, e.g [A]
to [SomeProtocol]
– this is covariance. It's worth noting that arrays have always been an edge case here, as arbitrary generics are invariant. Certain collections, such as Array
, just get special treatment from the compiler allowing for covariance.
It can deal with conversions of functions with super-typed parameters to functions with sub-typed parameters, e.g (SomeProtocol) -> Void
to (A) -> Void
– this is contravariance.
However it appears that it currently cannot do both in one go (but really it should be able to; feel free to file a bug).
For what it's worth, this has nothing to do with generics, the following reproduces the same behaviour:
protocol SomeProtocol {}
class A : SomeProtocol {}
func f1(listener: (A) -> Void) {}
func f2(listener: ([A]) -> Void) {}
func f3(listener: () -> [SomeProtocol]) {}
func g() {
let l1: (SomeProtocol) -> Void = { _ in }
f1(listener: l1) // NO ERROR
let l2: ([SomeProtocol]) -> Void = { _ in }
f2(listener: l2)
// COMPILER ERROR: Cannot convert value of type '([SomeProtocol]) -> Void' to
// expected argument type '([A]) -> Void'
// it's the same story for function return types
let l3: () -> [A] = { [] }
f3(listener: l3)
// COMPILER ERROR: Cannot convert value of type '() -> [A]' to
// expected argument type '() -> [SomeProtocol]'
}
Until fixed, one solution in this case is to simply use a closure expression to act as a trampoline between the two function types:
// converting a ([SomeProtocol]) -> Void to a ([A]) -> Void.
// compiler infers closure expression to be of type ([A]) -> Void, and in the
// implementation, $0 gets implicitly converted from [A] to [SomeProtocol].
f2(listener: { l2($0) })
// converting a () -> [A] to a () -> [SomeProtocol].
// compiler infers closure expression to be of type () -> [SomeProtocol], and in the
// implementation, the result of l3 gets implicitly converted from [A] to [SomeProtocol]
f3(listener: { l3() })
And, applied to your code:
f2(ofType: A.self, listener: { l2($0) })
This works because the compiler infers the closure expression to be of type ([T]?) -> Void
, which can be passed to f2
. In the implementation of the closure, the compiler then performs an implicit conversion of $0
from [T]?
to [SomeProtocol]?
.
And, as Dominik is hinting at, this trampoline could also be done as an additional overload of f2
:
func f2<T : SomeProtocol>(ofType type: T.Type, listener: ([SomeProtocol]?) -> Void) {
// pass a closure expression of type ([T]?) -> Void to the original f2, we then
// deal with the conversion from [T]? to [SomeProtocol]? in the closure.
// (and by "we", I mean the compiler, implicitly)
f2(ofType: type, listener: { (arr: [T]?) in listener(arr) })
}
Allowing you to once again call it as f2(ofType: A.self, listener: l2)
.
这篇关于Swift泛型:不能将类型的值转换为预期的参数类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!