无法将类实例分配给其协议类型?

Ras*_*sto 5 generics compiler-errors interface ios swift

请参阅下面的自包含示例.编译器在最后一行(标记为COMPILE ERROR)上报告错误,其中我将SimpleTrain协议类型的实例分配给它(根据我的最佳判断)符合.我怎样才能编译?我究竟做错了什么?或者这个编译器问题?

protocol Train {
    typealias CarriageType

    func addCarriage(carriage: CarriageType)
    func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType
}

class SimpleTrain<T> : Train {
    typealias CarriageType = T
    private var carriages: [T] = [T]()

    func addCarriage(carriage: T) {
       carriages.append(carriage)
    }

    func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType {
        let short = SimpleTrain<T>()
        short.addCarriage(carriages[0])
        return short //COMPILE ERROR: SimpleTrain<T> is not convertible to 'ShortType'
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑:即使我明确地向下转换shortTrain上面的返回类型(以便上面的代码片段的最后一行读取return short as ShortType),如安东尼奥建议的那样,在调用函数时仍然存在编译错误shortTrain:

let s = SimpleTrain<String>()
s.addCarriage("Carriage 1")
s.addCarriage("Carriage 2")

let a = s.shortTrain() //ERROR: Cannot convert the expression's type '()' to type 'Train'
let b = s.shortTrain<SimpleTrain<String>>() //ERROR: cannot explicitly specialize a generic function
Run Code Online (Sandbox Code Playgroud)

Rob*_*ier 4

首先,您要阅读规范线程devforums 上关于此问题的您特别想跳过来阅读 jckarter 的评论。

现在来回答编辑后的问题:

let a = s.shortTrain() //ERROR: Cannot convert the expression's type '()' to type 'Train'
Run Code Online (Sandbox Code Playgroud)

这是因为您没有为编译器提供足够的信息来确定a. 仔细思考它所看到的:

func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType {
let a = s.shortTrain()
Run Code Online (Sandbox Code Playgroud)

编译器需要在编译时找出类型a,它无法处理抽象类型。它需要一个完全指定的类型ShortType(一切都确定下来;指定所有泛型,解析所有类型别名)。它环顾四周,看到了一些对 的约束ShortType,但它没有看到任何实际给出类型的东西。它所拥有的只是a,这并没有给它任何提示。

不幸的是,这使得你必须明确地告诉它你想要发生什么。

let a: SimpleTrain<String> = s.shortTrain()
Run Code Online (Sandbox Code Playgroud)

这可能与您想要的相反,但这就是您现在可以在 Swift 中做的所有事情。Swift 团队(多次)表示,他们很清楚相关类型的这些问题(以及类型系统中的其他几个相关弱点)。他们特别意识到 Scala 类型系统可以处理这些事情,并且与当前的 Swift 类型系统有很多共同点(尽管根据我的经验,让复杂的路径依赖关联类型在 Scala 中工作也可能会导致头发撕裂) )。

也就是说,从您的示例中并不完全清楚您计划使用此功能做什么。有些火车会返回不同的类型作为它们的shortTrain()吗?

我发现这些问题在一般情况下经常会爆发,但在您面前的应用程序的特定情况下往往是可以解决的。在 Swift 中用代码构建真正任意的类型来解决所有问题是很困难的,但当你专注于你真正需要的类型时,它通常会奏效。例如,如果shortTrain()返回Self,这显然会变得更简单。如果调用者知道所需的结果类型,那么 aninit(shorten:)可能可以处理它。像这样的协议方法shortCarriages() -> [CarriageType]可以提供一个很好的桥梁。在设计上保持灵活性,其中之一几乎肯定会奏效。