Hi everyone. The review of SE-0467: MutableSpan begins now and runs through March 25, 2025.
Reviews are an important part of the Swift evolution process. All review feedback should be either on this forum thread or, if you would like to keep your feedback private, directly to the review manager via the forum messaging feature. When contacting the review manager directly, please keep the proposal link at the top of the message.
Trying it out
If you'd like to try this proposal out, you can download a toolchain supporting it:
The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:
What is your evaluation of the proposal?
Is the problem being addressed significant enough to warrant a change to Swift?
Does this proposal fit well with the feel and direction of Swift?
If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
A general collection is not contiguous, and therefore cannot vend a MutableSpan property over its contents. It would be feasible to provide either an optional mutable span (a la withContiguousMutableStorageIfAvailable),¹ or a closure-taking operation, but it likely makes sense to delay designing that to go with a more systematic proposal rethinking collections to account for ~Copyable/~Escapable constraints.
I'm still holding out hope that we formalize a ContiguousCollection protocol that guarantees this property--then it could provide span by default as well as mutableSpan when & MutableCollection.
With MutableSpan, we're introducing a new reference-semantics struct to the standard library that represents a mutable variant of another immutable type. Unlike Unsafe(Raw)Pointer and UnsafeMutable(Raw)Pointer, however, there's (as written) no implicit conversion from MutableSpan to Span. Having to use .span instead is a fairly minor ergonomics issue, but since this is expected to be a currency type I think it's worth at least noting.
If the language supported it, I'd imagine MutableSpan would be a subtype of Span, with a declaration of something like:
I've talked before on the forums about why I think subtype relationships between structs would be useful and natural, and this is another example of that. While that's not an entirely trivial feature to design, I'd hope that we wouldn't be blocked out of eventually supporting a true subtyping relationship for MutableSpan and Span (e.g. it'd be logical, albeit not essential, for mutableSpan as? Span<Type> to work) because of ABI concerns.
Aside: another example of a mutable-immutable struct subtype relationship
The reason I'm picking up on this particular point is because I've been toying with compiling Swift to SPIR-V shaders (based on the Embedded/no-allocation efforts, with a very basic example working), and realised there's the same Buffer<T>/MutableBuffer<T> (or StructuredBuffer/RWStructuredBuffer in HLSL naming) pattern there that would also benefit from subtyping. I don't think mutability is the only axis on which this is useful, though.
Speaking personally and not as review manager, one concern I have with introducing an implicit conversion (in addition to the usual ones) is that MutableSpan and Span are quite different in their API surface area, and where they have commonality, the second-effort effects of their APIs under lifetime checking are quite different due to the exclusive vs. shared borrowing behavior of the types. If an implicit conversion from MutableSpan to Span led to a downstream series of shared-ownership operations being accepted by the compiler, but an unrelated change causes the conversion to stop occurring and those operations now require exclusivity, it could take a while to track down the indirect cause of the resulting errors.
Though it supports most of the API surface of Span, MutableSpan isn't a subtype in one important respect: Span: Copyable, while MutableSpan: ~Copyable — this is where MutableSpan is less capable than Span. Converting explicitly from using MutableSpan to Span is a useful step to (1.) indicate what prevents mutating accesses to the MutableSpan binding, (2.) have a binding on which to attach copyability.