一尘不染

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

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'
    }
}

编辑: 即使我明确地向下转换了shortTrain上面的返回类型(以便上面的代码片段的最后一行读取return short asShortType),如安东尼奥在调用函数时仍然存在编译错误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

阅读 240

收藏
2020-07-07

共1个答案

一尘不染

首先,您想从devforums上读到规范的线程。您特别想跳过阅读jckarter的评论。

现在转到已编辑的问题:

let a = s.shortTrain() //ERROR: Cannot convert the expression's type '()' to type 'Train'

这是因为您没有给编译器足够的信息来确定的类型a。想一想它所看到的:

func shortTrain<ShortType: Train where ShortType.CarriageType == CarriageType>() -> ShortType {
let a = s.shortTrain()

编译器需要a在编译时确定类型,并且不能处理抽象类型。它需要一个完全指定的类型ShortType(确定所有内容;指定所有泛型,解析所有类型别名)。它环顾四周,并看到对的一些约束ShortType,但看不到实际提供类型的任何东西。它所具有的只是a,没有给出任何提示。

不幸的是,这使您不得不明确地告诉它要发生的事情。

let a: SimpleTrain<String> = s.shortTrain()

这可能与您想要的相反,但现在您可以在Swift中完成所有操作。Swift团队已经表明(很多次)他们非常了解与关联类型有关的这些问题(以及类型系统中的其他一些相关弱点)。他们特别了解可以处理这些事情的Scala类型系统,并且与当前的Swift类型系统有很多共同点(尽管以我的经验,在Scala中使用复杂的与路径相关的关联类型也可以导致头发撕裂)。

就是说,从您的示例中并不能完全清楚您计划如何使用此功能。某些火车会返回与shortTrain()不同的类型吗?

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

2020-07-07