Swift泛型:不能将类型的值转换为预期的参数类型 [英] Swift Generics: Cannot convert value of type to expected argument type

查看:473
本文介绍了Swift泛型:不能将类型的值转换为预期的参数类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是我的代码:

  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]?



,这个蹦床也可以作为 f2 的额外重载完成:

  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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆