【发布时间】:2016-11-09 08:28:20
【问题描述】:
我想创建如下协议:
protocol Parser {
func parse() -> ParserOutcome<?>
}
enum ParserOutcome<Result> {
case result(Result)
case parser(Parser)
}
我想让解析器返回特定类型的结果或另一个解析器。
如果我在Parser 上使用关联类型,那么我不能在enum 中使用Parser。如果我在parse() 函数上指定了泛型类型,那么我无法在没有泛型类型的实现中定义它。
我怎样才能做到这一点?
使用泛型,我可以这样写:
class Parser<Result> {
func parse() -> ParserOutcome<Result> { ... }
}
enum ParserOutcome<Result> {
case result(Result)
case parser(Parser<Result>)
}
这样,Parser 将由结果类型参数化。 parse() 可以返回 Result 类型的结果,或任何类型的解析器,该解析器将输出 Result 类型的结果,或由相同 Result 类型参数化的另一个解析器。
然而,据我所知,对于关联类型,我将始终有一个 Self 约束:
protocol Parser {
associatedtype Result
func parse() -> ParserOutcome<Result, Self>
}
enum ParserOutcome<Result, P: Parser where P.Result == Result> {
case result(Result)
case parser(P)
}
在这种情况下,我不能再使用任何类型的解析器来返回相同的 Result 类型,它必须是相同类型的解析器。
我希望使用Parser 协议获得与使用泛型定义相同的行为,并且我希望能够在类型系统的范围内做到这一点,而无需引入新的盒装类型,只需就像我可以使用正常的通用定义一样。
在我看来,在 Parser 协议中定义 associatedtype OutcomeParser: Parser,然后返回由该类型参数化的 enum 可以解决问题,但如果我尝试以这种方式定义 OutcomeParser,我会收到错误:
类型不能将自身作为要求引用
【问题讨论】:
-
我自己真的无法为此写出答案,但在我看来,您可能正在寻找类型擦除。我知道@RobNapier 在其中一些答案中已经展示了它的优雅使用,也许你可以在那里找到一些要调查的东西。
-
我正在寻找语言支持。如果它需要 hack,那么我宁愿不实现它。
-
我不认为类型擦除被认为是一种 hack,而是一种技术(我自己还没有掌握 :)。我想我在某处读到 Swift stdlib 本身在某些地方使用了类型擦除。 (此外,由于我自己并没有真正掌握类型擦除,因此在这种情况下使用它可能完全不合适。
-
嗯,确实,
AnySequence正在使用类型擦除,它在标准库中,并且被 Apple 明确记录为“类型擦除”。到目前为止,它仍然感觉像是一个 hack,但我正在研究它。 -
类型擦除不是 hack。它们在 Swift 标准库中使用。你可以在这里阅读更多关于它们的信息 - natashatherobot.com/swift-type-erasure。
标签: swift generics types swift-protocols associated-types