swift - 将不透明类型与关联类型一起使用

标签 swift swift-protocols opaque-result-type

假设我有一个协议(protocol)Parser,定义如下:

protocol Parser {
  associatedtype Element
  associatedtype Stream
  func parse(_ stream: Stream) -> (Element, Stream)?
}

现在让我们使用以下结构来遵守此协议(protocol):

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的“更具体”的协议(protocol),例如StringParser,在相同类型约束中设置关联类型。不幸的是,我并不是特别喜欢这种方法,因为我觉得如果我要定义返回具有更复杂的类型约束的 Parser 实例的其他方法,它不会很好地扩展。换句话说,我会用公开特定类型(例如 SomeSpecificParserType)与公开特定协议(protocol)(例如 SomeSpecificParserProtocol)进行交换,而理想情况下我希望保持在更高的抽象级别仅处理某些解析器返回类型。

有什么方法可以提供额外的信息来指定我从属性返回的类型的关联类型仅在属性定义上为 String ,以便 Swift 稍后可以推断出具体类型p.parse

最佳答案

在以具有关联类型的协议(protocol)形式使用不透明返回类型时,Swift 尚未提供很好的支持。原因之一是,当涉及到返回此类值时,您还无法描述协议(protocol)的关联类型。因此,编译器无法推断关联类型要使用什么。

如果您需要隐藏底层解析器,为了保留它的实现细节,一种解决方案是使用类型橡皮擦:

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

关于swift - 将不透明类型与关联类型一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58273332/

相关文章:

ios - 无法使用 FCM 在前台接收推送通知

ios - 如何在 iOS 中为自定义属性设置动画

ios - Swift 3.0 允许权限警报不间断

ios - NSView setNeedsDisplay 不可用

objective-c - 在 Swift 中传递 @protocol 类型

arrays - 返回一个不透明类型的数组

swift - AnyObject如何遵守NSObjectProtocol?

swift - Swift 协议(protocol)扩展中是否允许混合类/协议(protocol)类型约束?

ios - Text 或 Image 等 View 类型如何符合 SwiftUI 中的 View 协议(protocol)?