SE-0485: OutputSpan: delegate initialization of contiguous memory

I'm +1 overall on the proposal. I think OutputSpan and RawOutputSpan solve meaningful problems. However, I'd like to call out the following points, which IMO aren't super visible from the proposal:

OutputSpan is sort of halfway between value semantics and reference semantics: it depends on existing memory and has a "reference" to it, but the number of initialized values in the referenced storage is squarely a value of the OutputSpan. One of the resulting oddities, if you'll allow C++ nomenclature, is that you can turn a "const OutputSpan" into a mutable one just with a consuming assignment from a let to a var.

A consequence of OutputSpan requiring mutability is that it must typically be passed as an inout value. However, this means OutputSpan could hypothetically also be reinitialized to somebody else's underlying storage, in which case you end up in a situation that doesn't really make sense anymore. This is intended to be caught with finalize, which requires you to pass back in the original storage pointer to confirm you have the right OutputSpan.

At the same time, making OutputSpan a reference type is not the obviously correct thing to do either, because that would allow you to alias the storage. With aliasing storage, span and mutableSpan need a runtime check to verify you have exclusive access. (It's also not a given that @lifetime currently works on reference types!)

If I understood correctly this interaction, when you don't call finalize() on OutputSpan, on deinit, it will deinitialize all elements that were added. However, it won't revert any other changes, including modifications to existing elements or removed elements. (Also, how does it do that? The detailed design doesn't seem to specify that it remembers the original count.)

3 Likes