定义将集合保存为属性的协议



我想定义一个协议,该协议定义一个类型为"集合"的变量"序列"。我想要它,因为我有几个符合的类型,它们都将包含不同类型的数组。例:

protocol BioSequence {
associatedtype T
var sequence: Collection { get }
// Will also implement conformance to Collection and redirect all Collection functions to the property "sequence". This is because my types that conform to BioSequence are just wrappers for an array of Nucleotide or AminoAcid with some extra functionality
}
struct DNASequence: BioSequence {
// Will hold "sequence" of type [Nucleotide]
}
struct AminoSequence: BioSequence {
// Will hold "sequence" of type [AminoAcid]
}

我为什么要这个?因为我只需要在 BioSequence 中实现一次对"集合"的一致性,并且所有符合类型都会自动继承它。另外,我可以在符合类型上自由添加额外的功能。

现在,当我像上面的代码一样尝试这样做时,编译器说:"协议集合只能用作通用约束"。是的,我在谷歌上搜索了这个错误的含义,但是我如何实际修复它以使我的代码像我想要的那样工作。或者甚至不可能做我想做的事?

谢谢。

您可以通过在协议中使用associatedtype轻松实现这一点,该可以限制为Collection,允许符合类型在采用协议时满足具体类型的要求。

例如:

protocol CollectionWrapper : Collection {
associatedtype Base : Collection
var base: Base { get }
}
extension CollectionWrapper {
var startIndex: Base.Index {
return base.startIndex
}
var endIndex: Base.Index {
return base.endIndex
}
func index(after i: Base.Index) -> Base.Index {
return base.index(after: i)
}
subscript(index: Base.Index) -> Base.Iterator.Element {
return base[index]
}
// Note that Collection has default implementations for the rest of the
// requirements. You may want to explicitly implement them and forward them to
// the base collection though, as it's possible the base collection implements
// them in a more efficient manner (e.g being non random access and having
// a stored count).
}
// S adopts CollectionWrapper and satisfies the 'Base' associatedtype with [String].
struct S: CollectionWrapper {
var base: [String]
}
let s = S(base: ["foo", "bar", "baz"])
print(s[1]) // "bar"

关于您的评论:

如果我想这样使用它:let a: CollectionWrapper = S()[...] 这让我再次面临"协议只能用作通用约束"。

问题是您目前不能谈论具有相关类型要求的协议,因为编译器不知道用于满足这些需求的类型(尽管这不是技术限制)。你可以通过使用 Swift 的AnyCollection类型橡皮擦来包装具有给定元素类型的任意Collection来解决此问题。

例如:

let a = AnyCollection(S(base: ["foo", "bar", "baz"]))

如果您需要处理CollectionWrapper中的其他协议要求,则必须实现自己的类型橡皮擦才能执行此操作。请参阅此处 Rob 的回答,了解如何执行此操作。

最新更新