假设我有一个协议Parser
,定义如下:
protocol Parser {
associatedtype Element
associatedtype Stream
func parse(_ stream: Stream) -> (Element, Stream)?
}
现在让我们使用以下结构来遵守该协议:
struct CharacterParser: Parser {
let character: Character
func parse(_ stream: String) -> (Character, String)? {
guard stream.first == character
else { return nil }
return (character, String(stream.dropFirst()))
}
}
现在,我们可以为Character
写一个整齐的扩展名以创建字符解析器:
extension Character {
var parser: CharacterParser { return CharacterParser(character: self) }
}
let p = Character("a").parser
print(p.parse("abc"))
// Prints `Optional(("a", "bc"))`
现在让我们说我想隐藏解析器实现的细节,并使用Swift 5.1中的新不透明类型。编译器将让我编写以下代码:
@available(OSX 10.15.0, *)
extension Character {
var parser: some Parser { return CharacterParser(character: self) }
}
到目前为止,一切都很好。但是现在无法调用parse(:)
,因为似乎编译器不再能够解析我应该提供的参数类型。换句话说,以下内容将无法编译:
let p = Character("a").parser
print(p.parse("abc"))
// error: Cannot invoke 'parse' with an argument list of type '(String)'
我发现的唯一解决方案是定义一个继承自Parser
的“更具体”协议,例如StringParser
,用于在同类型约束中设置关联的类型。不幸的是,我并不特别喜欢这种方法,因为如果我定义其他方法来返回带有更复杂的类型约束的Parser
实例,我认为它无法很好地扩展。换句话说,我将交换暴露特定类型(例如SomeSpecificParserType
)与暴露特定协议(例如SomeSpecificParserProtocol
),而我希望保持较高的抽象级别,理想情况下仅处理{{ 1}}返回类型。
有什么方法可以提供其他信息,以指定我从属性返回的类型的关联类型仅是属性定义上的some Parser
,以便Swift以后可以推断出{{ 1}}?
答案 0 :(得分:1)
在使用带有相关类型的协议形式的不透明返回类型时,Swift还没有很好的支持。原因之一是,在返回此类值时,您尚无法描述协议的关联类型。因此,编译器无法推断对关联类型使用什么。
如果需要隐藏基础解析器,以使其保持实现细节,一种解决方案是使用类型橡皮擦:
struct AnyParser<Element, Stream>: Parser {
private var _parse: (Stream) -> (Element, Stream)?
init<P: Parser>(_ parser: P) where P.Element == Element, P.Stream == Stream {
_parse = parser.parse
}
func parse(_ stream: Stream) -> (Element, Stream)? {
return _parse(stream)
}
}
extension Character {
var parser: AnyParser<Self, String> { return .init(CharacterParser(character: self)) }
}