【问题标题】:How to make a protocol with a generic array-like property如何制作具有通用类数组属性的协议
【发布时间】:2020-08-04 16:18:36
【问题描述】:

我正在创建一个协议来抽象不同的模型(手工制作、可解码和节俭生成),如下所示:

protocol FeedItemModeling {
    var text: String? { get }
    var url: URL? { get }
}

protocol FeedModeling {
    var items: [FeedItemModeling]? { get }
}

这会很好用,除了 thrift 模型不使用基本数组,而是使用符合 RandomAccessCollectionTList 等等。由于ArrayTList 都符合RandomAccessCollection,我正在尝试将协议定义更改为:

尝试 A)

var items: RandomAccessCollection<FeedItemModeling>? { get }

这给出了错误:“不能专门化非泛型类型 'RandomAccessCollection'。”我认为这是因为associatedtype 是一个半生不熟的泛型,因为它可以在协议本身内部强制执行约束,但不会暴露在外部;这意味着在我们的例子中,RandomAccessCollectionassociatedtypesRandomAccessCollection 内解析,但FeedModeling 无法访问它?

尝试 B)

var items: RandomAccessCollection? { get }

给出“Protocol 'RandomAccessCollection' 只能用作通用约束,因为它具有 Self 或关联的类型要求”并且无论如何都会失去对 FeedItemModeling 的约束。

尝试 C)

associatedtype C: RandomAccessCollection
var items: C? { get }

有效,但我们失去了对 FeedItemModeling 的约束,所以这是不可接受的。

尝试 D)

associatedtype C: RandomAccessCollection
var items: C<FeedItemModeling>? { get }

给出“不能专门化非泛型类型‘Self.C’。”我想是和上面一样的原因吧?

尝试 E)

associatedtype C<E>: RandomAccessCollection where E: FeedItemModeling
var items: C? { get }

给出“关联类型不能有泛型参数列表”。我猜是因为现在约束上有个约束,不能这样表达?

尝试 F)

typealias C<E> = RandomAccessCollection
var items: C<FeedItemModeling>? { get }

给出:“协议 'RandomAccessCollection' 只能用作通用约束,因为它具有 Self 或关联的类型要求”。同样的原因?

尝试 G)

associatedtype FeedItemModelingList = RandomAccessCollection where RandomAccessCollection.Element: FeedItemModeling
var items: FeedItemModelingList? { get }

给出“关联类型‘元素’只能与具体类型或泛型参数库一起使用”。我不明白这个;请告诉我为什么?

尝试 H)

protocol FeedItemModelingList: RandomAccessCollection {}
...
var items: FeedItemModelingList? { get }

给出:“协议 'FeedItemModelingList' 只能用作通用约束,因为它具有 Self 或关联的类型要求”。

尝试一)

protocol FeedItemModelingList: RandomAccessCollection where Element == FeedItemModeling {}
...
associatedtype L = FeedItemModelingList
var items: L? { get }

似乎工作(到目前为止),但真的很丑。

有更好的主意吗?

【问题讨论】:

    标签: swift generics collections protocols


    【解决方案1】:

    我想你想要的是

    protocol FeedItemModeling {
        var text: String? { get }
        var url: URL? { get }
    }
    
    protocol FeedModeling {
        associatedtype TList: RandomAccessCollection where TList.Element == FeedItemModeling
        var items: TList? { get }
    }
    

    至于你的问题:

    我认为这是因为 associatedtype 是半生不熟的泛型

    我认为在现代 Swift 中,关联类型与泛型非常接近,尽管两者都有些不完整,尤其是在存在主义方面,尽管它每年都在变得更好。一条经验法则是,where 解决了很多 Swift 泛型/PAT 问题。

    给出“不能专门化非泛型类型‘Self.C’。”我想是和上面一样的原因吧?

    IMO 这个错误很糟糕,但它基本上是关于&lt;&gt; 的语法问题,它只允许用于泛型类型而不是协议。 “专业化”在 Swift 社区中是一个有点重载的术语,可以表示从类型约束到为已知类型参数值发出机器代码的任何内容。

    给出“关联类型不能有泛型参数列表”。我猜是因为现在约束上有个约束,不能这样表达?

    你可以在一个约束上表达一个约束,但是没有像 rust 一样的 &lt;F=A&gt; 语法 yet。目前,您需要where

    只能用作通用约束,因为它具有 Self 或关联的类型要求”。

    这是臭名昭著的“PAT”或“广义存在”问题(参见泛型宣言)。每个人都对此感到恼火,希望有一天我们会支持它。

    “关联类型‘元素’只能与具体类型或泛型参数库一起使用”。我不明白这个;请告诉我为什么?

    RandomAccessCollection 只是一些协议,你碰巧在附近使用,但 Swift 不知道连接。所以当你这么说的时候

    associatedtype FeedItemModelingList = RandomAccessCollection where RandomAccessCollection.Element: FeedItemModeling
    

    有点像你做的

    func foo() where Collection.Element == String
    

    Collection.Element 在这种情况下是“封闭的”,没有办法专门化它或给它一个约束。相反,您限制了一些开放的东西,例如您要声明的关联类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-10-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-24
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多