What are good ways to implement an iterator that can throw?

I'm pretty new to Swift, so it's possible I'm missing something, but from what I've found searching this forum and other places, it seems like the inability to make iterators throw is a known issue. It looks like it's been solved for async iterators, but sync ones are still affected for backwards-compatibility reasons. But most of the posts I find are a few years old and so might be out of date.

Assuming there's no one-size-fits-all solution that I just haven't found yet, what options are there? The only thing I can think of is to convert it to an async iterator, but given that I don't actually need asynchronicity, that seems suboptimal.

Unsure if it's the most idiomatic way, but you could always wrap your Elements into Results

1 Like

What is the actual problem you're trying to solve by using a throwing iterator?

I've needed throwing iterators for UTF8 decoding. It would be nice to be able to have a sequence which lazily goes from UTF8 bytes -> Unicode Scalars.

The way I've done it is to include an error property:

struct UTF8Decoder<Source: IteratorProtocol<UInt8>>: IteratorProtocol {

  var source: Source
  var decoder = UTF8()
  var error = false

  mutating func next() -> Unicode.Scalar? {
      guard !error else {
          return nil
      }
      switch decoder.decode(&source) {
      case .scalarValue(let scalar): return scalar
      case .emptyInput: return nil
      case .error: error = true; return nil
      }
  }
}


var test = Array("🀧πŸ₯³πŸ€™β‚©β‚²hello".utf8)
test += [0x99, 0x42]

var decoder = UTF8Decoder(source: test.makeIterator())
while let scalar = decoder.next() {
    print(scalar)
}
if decoder.error {
  print("Invalid UTF8")
}

That doesn't compose very well, but it's generally been sufficient for the things I've needed. If you wanted to do something better, you could have the decoder invoke a callback closure and capture the flag in to some external state.

None of these options are really great though, I'm afraid. My hopes are all on the collection redesign @lorentey is cooking up having support for throwing sequences.

2 Likes