From 55c0575ab2a65f2119c60608171d5408b4b11982 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 22 Sep 2026 07:38:10 -0700 Subject: [PATCH 1/6] [pitch] MutableSpan bulk copying API --- proposals/nnnn-mutablespan-bulk-copies.md | 488 ++++++++++++++++++++++ 1 file changed, 488 insertions(+) create mode 100644 proposals/nnnn-mutablespan-bulk-copies.md diff --git a/proposals/nnnn-mutablespan-bulk-copies.md b/proposals/nnnn-mutablespan-bulk-copies.md new file mode 100644 index 0000000000..1ace285a47 --- /dev/null +++ b/proposals/nnnn-mutablespan-bulk-copies.md @@ -0,0 +1,488 @@ +# Bulk copying operations for `MutableSpan` and `MutableRawSpan` + +* Proposal: [SE-NNNN](nnnn-mutablespan-bulk-copies.md) +* Author: [Guillaume Lessard](https://github.com/glessard) +* Review Manager: TBD +* Status: **Awaiting review** +* Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) +* Implementation: [swiftlang/swift#92466](https://github.com/swiftlang/swift/pull/92466) +* Previous Proposal: Follow-up to [SE-0467](0467-MutableSpan.md) +* Review: ([pitch](https://forums.swift.org/...)) + +[SE-0370]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0370-pointer-family-initialization-improvements.md +[SE-0467]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0467-MutableSpan.md +[SE-0485]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0485-outputspan.md +[SE-0516]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0516-borrowing-sequence.md +[SE-0527]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0527-rigidarray-uniquearray.md + +## Summary of changes + +Adds bulk-update operations to `MutableSpan` and `MutableRawSpan`, which overwrite a span (or a subrange thereof) with a repeated value, or with the elements of another span or an `Iterable`. + +## Motivation + +[SE-0467][SE-0467] introduced `MutableSpan` and `MutableRawSpan` as the safe, composable replacements for `UnsafeMutableBufferPointer` and `UnsafeMutableRawBufferPointer`. Bulk operations were missing from their original implementation, and they are necessary for the safe types to supersede their unsafe predecessors in most uses. + +We propose adding bulk copy operations that overwrite elements of a `MutableSpan`. With the proposed changes, copying elements from a `Span` to a `MutableSpan` becomes a single call that uses bulk memory operations: + +```swift +// was (when using exclusively safe code): +precondition(source.count == destination.count) +for i in destination.indices { + destination[i] = source[i] +} + +// proposed: +destination.updateAll(copying: source) +``` + +## Proposed solution + +We propose a family of `update` operations on `MutableSpan` and `MutableRawSpan`. + +The family is composed of three base method names: + +- `updateAll(...)` overwrites every element of the destination span. The source must contain exactly as many elements as the destination. +- `updateSubrange(_:...)` overwrites every element in the given range of indices. The source must contain exactly as many elements as the range does. +- `updateElements(from:...)` overwrites the destination's elements starting at the given index, and reports the index after the last element updated. + +The source argument of these methods can have three different labels, depending on the kind of source: + +- `repeating:` sets every destination element to the same value. +- `copying:` copies elements from a source. +- `moving:` moves elements from an `OutputSpan`, leaving its memory uninitialized. + +For example: + +```swift +var destination = array.mutableSpan + +destination.updateAll(repeating: 0) +destination.updateSubrange(2..<6, repeating: -1) +destination.updateAll(copying: source.span) +destination.updateSubrange(2..<6, copying: source.span) +``` + +`updateAll` and `updateSubrange` require the source count to match the destination count or the subrange count, and trap when the counts don't match. + +#### Copying from an `Iterable` source + +`updateElements(from:copying:)` handles the case where the amount of data from the source is not known ahead of time. It takes any [`Iterable`][SE-0516] source, such as `Span`, `InlineArray`, and `UniqueArray`: + +```swift +var destination = array.mutableSpan +var index = 0 +destination.updateElements(from: &index, copying: header) +destination.updateElements(from: &index, copying: payload) +destination.updateElements(from: &index, copying: checksum) +precondition(index == destination.count) +``` + +The overload used above accepts any `Iterable`, including one whose iteration can throw. The `index` is taken as an `inout` parameter because a thrown error must not leave the destination in an unknown state: when the function returns, either normally or by throwing, `index` is updated to the index after the last element updated. + +When using an `Iterable` whose `Failure` is `Never`, a convenience overload of `updateElements` is available. It returns the index after the last element updated: + +```swift +var end = destination.updateElements(from: 0, copying: header) +end = destination.updateElements(from: end, copying: payload) +``` + +The overloads that take `some Iterable` copy their source completely, and trap if the span cannot contain every element from the source. The source can be shorter than the remaining space. + +A third overload mutates a [`BorrowingIteratorProtocol`][SE-0516] source, and stops as soon as either the iterator has provided all its elements or the end of the destination span is reached, whichever comes first. When the function returns (normally or by throwing), the iterator is positioned after the last element it provided to the destination. + +```swift +var iterator = source.makeBorrowingIterator() +var i = 0 +first.updateElements(from: &i, copying: &iterator) +if i == first.count { + i = 0 + second.updateElements(from: &i, copying: &iterator) +} +``` + +#### Moving elements + +`updateAll(moving:)` and `updateSubrange(_:moving:)` mutate an [`OutputSpan`][SE-0485], and move its elements into the destination. The source `OutputSpan` is empty afterwards, with its memory uninitialized. + +```swift +var destination = storage.mutableSpan +source.edit { output in + destination.updateSubrange(2..<4, moving: &output) +} +assert(source.isEmpty) +``` + +Moving an element doesn't require its type to be copyable, so the `moving:` variants are available when `Element: ~Copyable`. + +## Detailed design + +### `MutableSpan` + +```swift +extension MutableSpan { + /// Overwrites every element of this span with the given value. + /// + /// - Parameter repeatedValue: The value to set for every element. + mutating func updateAll(repeating repeatedValue: consuming Element) + + /// Overwrites every element within a range of indices with the given value. + /// + /// - Parameters: + /// - subrange: A valid range of indices. Every index in this range + /// must be within the bounds of this `MutableSpan`. + /// - repeatedValue: The value to set for every element in `subrange`. + mutating func updateSubrange( + _ subrange: Range, repeating repeatedValue: consuming Element + ) + + mutating func updateSubrange( + _ subrange: some RangeExpression, repeating repeatedValue: consuming Element + ) + + mutating func updateSubrange( + _ subrange: UnboundedRange, repeating repeatedValue: consuming Element + ) + + /// Overwrites every element of this span by copying the elements + /// of the source. + /// + /// `source` must have exactly as many elements as this span. + /// + /// - Parameter source: The elements to copy into this span. + mutating func updateAll(copying source: Span) + + /// Overwrites the elements within a range of indices by copying + /// the elements of the source. + /// + /// `source` must have exactly as many elements as `subrange`. + /// + /// - Parameters: + /// - subrange: A valid range of indices. Every index in this range + /// must be within the bounds of this `MutableSpan`. + /// - source: The elements to copy into `subrange`. + mutating func updateSubrange( + _ subrange: Range, copying source: Span + ) + + mutating func updateSubrange( + _ subrange: some RangeExpression, copying source: Span + ) + + mutating func updateSubrange( + _ subrange: UnboundedRange, copying source: Span + ) +} + +extension MutableSpan where Element: ~Copyable { + /// Overwrites every element of this span by moving the elements + /// from the source. + /// + /// `source` must have exactly as many initialized elements as this span. + /// When this function returns, `source` is empty, and its memory has been + /// returned to the uninitialized state. + /// + /// - Parameter source: The elements to move into this span. + mutating func updateAll(moving source: inout OutputSpan) + + /// Overwrites the elements within a range of indices by moving + /// the elements from the source. + /// + /// `source` must have exactly as many initialized elements as `subrange`. + /// When this function returns, `source` is empty, and its memory has been + /// returned to the uninitialized state. + /// + /// - Parameters: + /// - subrange: A valid range of indices. Every index in this range + /// must be within the bounds of this `MutableSpan`. + /// - source: The elements to move into `subrange`. + mutating func updateSubrange( + _ subrange: Range, moving source: inout OutputSpan + ) + + mutating func updateSubrange( + _ subrange: some RangeExpression, moving source: inout OutputSpan + ) + + mutating func updateSubrange( + _ subrange: UnboundedRange, moving source: inout OutputSpan + ) +} + +@available(SwiftStdlib 6.4, *) +extension MutableSpan { + /// Overwrites elements of this span, starting at an index, by copying + /// every element of the source. + /// + /// This span must have enough space from `index` to its end + /// (`index..( + from index: inout Index, copying source: borrowing I + ) throws(I.Failure) where I.Element == Element + + /// Overwrites elements of this span, starting at an index, by copying + /// every element of the source. + /// + /// This span must have enough space from `index` to its end + /// (`index..( + from index: Index, copying source: borrowing I + ) -> Index where I.Element == Element, I.Failure == Never + + /// Overwrites elements of this span, starting at an index, by copying + /// elements from an iterator. + /// + /// Copying stops as soon as `source` has provided all its elements, + /// or the end of this span is reached, whichever comes first. + /// + /// If reading from `source` throws an error, the elements copied before + /// the error occurred remain in this span, and `index` is updated to + /// the index after the last element updated before the error. + /// + /// - Parameters: + /// - index: The index at which to start copying. It must be a valid + /// index of this span, or equal to its `count`. On return, it is + /// updated to the index after the last element updated. + /// - source: An iterator over the elements to copy into this span. On + /// return, it is positioned after the last element copied. + /// - Throws: Any error thrown while reading from `source`. + mutating func updateElements< + I: BorrowingIteratorProtocol & ~Escapable & ~Copyable + >( + from index: inout Index, copying source: inout I + ) throws(I.Failure) where I.Element == Element +} +``` + +### `MutableRawSpan` + +The `MutableRawSpan` operations are the same as the typed ones, replacing typed sources with `RawSpan` and `OutputRawSpan`. The `updateElements` overloads use an `Iterable` with an element type of `UInt8`. + +```swift +extension MutableRawSpan { + + /// Overwrites every byte of this span with the given value. + /// + /// - Parameter repeatedByte: The value to set for every byte. + mutating func updateAll(repeating repeatedByte: UInt8) + + /// Overwrites every byte within a range of offsets with the given value. + /// + /// - Parameters: + /// - subrange: A valid range of offsets. Every offset in this range + /// must be within the bounds of this `MutableRawSpan`. + /// - repeatedByte: The value to set for every byte in `subrange`. + mutating func updateSubrange( + _ subrange: Range, repeating repeatedByte: UInt8 + ) + + mutating func updateSubrange( + _ subrange: some RangeExpression, repeating repeatedByte: UInt8 + ) + + mutating func updateSubrange( + _ subrange: UnboundedRange, repeating repeatedByte: UInt8 + ) + + /// Overwrites every byte of this span by copying the bytes of the source. + /// + /// `source` must have exactly as many bytes as this span. + /// + /// - Parameter source: The bytes to copy into this span. + mutating func updateAll(copying source: RawSpan) + + /// Overwrites the bytes within a range of offsets by copying + /// the bytes of the source. + /// + /// `source` must have exactly as many bytes as `subrange`. + /// + /// - Parameters: + /// - subrange: A valid range of offsets. Every offset in this range + /// must be within the bounds of this `MutableRawSpan`. + /// - source: The bytes to copy into `subrange`. + mutating func updateSubrange( + _ subrange: Range, copying source: RawSpan + ) + + mutating func updateSubrange( + _ subrange: some RangeExpression, copying source: RawSpan + ) + + mutating func updateSubrange( + _ subrange: UnboundedRange, copying source: RawSpan + ) + + /// Overwrites every byte of this span by moving the bytes from the source. + /// + /// `source` must have exactly as many initialized bytes as this span. + /// When this function returns, `source` is empty, and its memory has been + /// returned to the uninitialized state. + /// + /// - Parameter source: The bytes to move into this span. + mutating func updateAll(moving source: inout OutputRawSpan) + + /// Overwrites the bytes within a range of offsets by moving + /// the bytes from the source. + /// + /// `source` must have exactly as many initialized bytes as `subrange`. + /// When this function returns, `source` is empty, and its memory has been + /// returned to the uninitialized state. + /// + /// - Parameters: + /// - subrange: A valid range of offsets. Every offset in this range + /// must be within the bounds of this `MutableRawSpan`. + /// - source: The bytes to move into `subrange`. + mutating func updateSubrange( + _ subrange: Range, moving source: inout OutputRawSpan + ) + + mutating func updateSubrange( + _ subrange: some RangeExpression, moving source: inout OutputRawSpan + ) + + mutating func updateSubrange( + _ subrange: UnboundedRange, moving source: inout OutputRawSpan + ) +} + +@available(SwiftStdlib 6.4, *) +extension MutableRawSpan { + + /// Overwrites bytes of this span, starting at a byte offset, by copying + /// every byte of the source. + /// + /// This span must have enough space from `byteOffset` to its end + /// (`byteOffset..( + from byteOffset: inout Int, copying source: borrowing I + ) throws(I.Failure) where I.Element == UInt8 + + /// Overwrites bytes of this span, starting at a byte offset, by copying + /// every byte of the source. + /// + /// This span must have enough space from `byteOffset` to its end + /// (`byteOffset..( + from byteOffset: Int, copying source: borrowing I + ) -> Int where I.Element == UInt8, I.Failure == Never + + /// Overwrites bytes of this span, starting at a byte offset, by copying + /// bytes from an iterator. + /// + /// Copying stops as soon as `source` has provided all its elements, + /// or the end of this span is reached, whichever comes first. + /// + /// If reading from `source` throws an error, the bytes copied before + /// the error occurred remain in this span, and `byteOffset` is updated to + /// the offset after the last byte updated before the error. + /// + /// - Parameters: + /// - byteOffset: The offset at which to start copying. It must be a valid + /// offset into this span, or equal to its `byteCount`. On return, it + /// is updated to the offset after the last byte updated. + /// - source: An iterator over the bytes to copy into this span. On + /// return, it is positioned after the last byte copied. + /// - Throws: Any error thrown while reading from `source`. + mutating func updateElements< + I: BorrowingIteratorProtocol & ~Escapable & ~Copyable + >( + from byteOffset: inout Int, copying source: inout I + ) throws(I.Failure) where I.Element == UInt8 +} +``` + +## Source compatibility + +This proposal is additive and source-compatible with existing code. + +## ABI compatibility + +The additions in this proposal will be implemented without creating additional ABI. + +These additions require the existence of `Span` or `Iterable`. On ABI-stable platforms, they have a minimum deployment target that matches the availability of `Span` or `Iterable`. + +## Implications on adoption + +The additions described in this proposal require a new version of the Swift standard library. + +## Future directions + +#### Bulk initialization for `OutputSpan` + +Bulk append operations for `OutputSpan` and `OutputRawSpan` are similar to the operations proposed here, but we are deferring them to a future proposal. The outcome of this proposal will inform a bulk-initialization proposal. + +#### Piecewise updating from a container of noncopyable elements + +We are proposing `updateElements(from:copying:)` to copy elements from an `Iterable`, but this is limited to copyable elements. The proposal doesn't include an `updateElements(from:moving:)` that would update a `MutableSpan` from a `UniqueDeque`, for example. The `DrainableContainer` protocol from the `ContainersPreview` module of [swift-collections](https://github.com/apple/swift-collections) is a prototype of the functionality needed for `updateElements(from:moving:)`. + +#### Generalized container protocols + +We need a generalization of the `Collection` family of protocols for noncopyable (and nonescapable) elements. In such a generalized family of protocols, the functions described here would become method requirements for a successor to `MutableCollection`. + +## Alternatives considered + +#### Using only `update` as the base name + +`updateAll(repeating:)` is a synonym for the existing `update(repeating:)`, and we considered using names such as `update(subrange:copying:)` for the methods proposed here. We went with `updateAll` and `updateSubrange` for two reasons. + +First, there are precedents in `replaceSubrange(_:with:)` and `removeSubrange(_:)` (in `RangeReplaceableCollection`), and `replaceSubrange(_:copying:)` (in [`UniqueArray`][SE-0527]), for situations that require a range parameter. `removeSubrange` is paired with `removeAll`. + +Second, the name of `update(repeating:)` came from the equivalent method of `UnsafeMutableBufferPointer` ([SE-0370][SE-0370]). `UnsafeMutableBufferPointer`'s `update` method doesn't need a "subrange" parameter because its slicing syntax is so compact. Unfortunately, that compact slicing syntax does not work with noncopyable containers. Given the requirement to pass a range as the first argument, we propose a naming model similar to `replaceSubrange` and `removeSubrange`. + +#### Deprecating the existing `update(repeating:)` + +`update(repeating:)` shipped in Swift 6.2 and now has a synonym in `updateAll(repeating:)`. The extra spelling is harmless, so we choose not to deprecate it. + +#### Closure-based bulk updates + +A `withMutableSubrange(_:) { ... }` form would scope the sub-span automatically and avoid needing to insert an explicit `consume` to end accesses. As discussed in [SE-0467][SE-0467], the `Span` family is deliberately avoiding new closure-taking APIs, as they compose poorly with each other and with new language features. + +#### Returning an iterator from `updateElements(from:copying:)` + +`UnsafeMutableBufferPointer.update(from:)` ([SE-0370][SE-0370]) is a close counterpart to `updateElements(from:copying:)`. The return type of the older method is a tuple of an iterator and an index. Returning state in this way is viable in non-throwing situations, but iteration over an `Iterable` can throw, and the state would be lost when an error is thrown. When the thrown type is `Never`, we would like to return the iterator in addition to the index, but we cannot do so at this time because tuples cannot contain nonescapable types. We provide an overload that takes an `inout some BorrowingIteratorProtocol` as a replacement for a tuple-returning function. + +## Acknowledgements + +Thanks to Karoy Lorentey for his work on prototyping the container protocols in the [swift-collections](https://github.com/apple/swift-collections) package. From d5313aec669a28b78b752a519b6f7520cf8e461f Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 28 Sep 2026 15:59:32 -0700 Subject: [PATCH 2/6] Address review feedback --- ...es.md => nnnn-mutablespan-bulk-updates.md} | 113 +++++++++++------- 1 file changed, 67 insertions(+), 46 deletions(-) rename proposals/{nnnn-mutablespan-bulk-copies.md => nnnn-mutablespan-bulk-updates.md} (72%) diff --git a/proposals/nnnn-mutablespan-bulk-copies.md b/proposals/nnnn-mutablespan-bulk-updates.md similarity index 72% rename from proposals/nnnn-mutablespan-bulk-copies.md rename to proposals/nnnn-mutablespan-bulk-updates.md index 1ace285a47..fde4115015 100644 --- a/proposals/nnnn-mutablespan-bulk-copies.md +++ b/proposals/nnnn-mutablespan-bulk-updates.md @@ -1,12 +1,12 @@ -# Bulk copying operations for `MutableSpan` and `MutableRawSpan` +# Bulk update operations for `MutableSpan` and `MutableRawSpan` -* Proposal: [SE-NNNN](nnnn-mutablespan-bulk-copies.md) +* Proposal: [SE-NNNN](nnnn-mutablespan-bulk-updates.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: TBD * Status: **Awaiting review** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Implementation: [swiftlang/swift#92466](https://github.com/swiftlang/swift/pull/92466) -* Previous Proposal: Follow-up to [SE-0467](0467-MutableSpan.md) +* Previous Proposal: [SE-0467](0467-MutableSpan.md) * Review: ([pitch](https://forums.swift.org/...)) [SE-0370]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0370-pointer-family-initialization-improvements.md @@ -17,13 +17,15 @@ ## Summary of changes -Adds bulk-update operations to `MutableSpan` and `MutableRawSpan`, which overwrite a span (or a subrange thereof) with a repeated value, or with the elements of another span or an `Iterable`. +Adds bulk update operations to `MutableSpan` and `MutableRawSpan`, which overwrite a span (or a subrange thereof) with a repeated value, or with the elements of another span or an `Iterable`. ## Motivation -[SE-0467][SE-0467] introduced `MutableSpan` and `MutableRawSpan` as the safe, composable replacements for `UnsafeMutableBufferPointer` and `UnsafeMutableRawBufferPointer`. Bulk operations were missing from their original implementation, and they are necessary for the safe types to supersede their unsafe predecessors in most uses. +[SE-0467][SE-0467] introduced `MutableSpan` and `MutableRawSpan` as the safe, composable replacements for `UnsafeMutableBufferPointer` and `UnsafeMutableRawBufferPointer`. Bulk operations were deferred[^1] from the original proposal, yet they are necessary for the safe types to supersede their unsafe predecessors. -We propose adding bulk copy operations that overwrite elements of a `MutableSpan`. With the proposed changes, copying elements from a `Span` to a `MutableSpan` becomes a single call that uses bulk memory operations: +[^1]: [SE-0467][SE-0467] proposed bulk update functions, but [SE-0485][SE-0485] withdrew them in favor of proposing a better naming scheme at a later time. + +We propose adding bulk update operations that overwrite existing elements of a `MutableSpan`. With the proposed changes, copying elements from a `Span` to a `MutableSpan` becomes a single call that uses bulk memory operations: ```swift // was (when using exclusively safe code): @@ -38,7 +40,7 @@ destination.updateAll(copying: source) ## Proposed solution -We propose a family of `update` operations on `MutableSpan` and `MutableRawSpan`. +We propose a family of `update` operations on `MutableSpan` and `MutableRawSpan`. They overwrite a range of contiguous elements, deinitializing the overwritten values. The family is composed of three base method names: @@ -46,28 +48,32 @@ The family is composed of three base method names: - `updateSubrange(_:...)` overwrites every element in the given range of indices. The source must contain exactly as many elements as the range does. - `updateElements(from:...)` overwrites the destination's elements starting at the given index, and reports the index after the last element updated. -The source argument of these methods can have three different labels, depending on the kind of source: +The source argument of each method can have three different labels, depending on the kind of source: - `repeating:` sets every destination element to the same value. - `copying:` copies elements from a source. - `moving:` moves elements from an `OutputSpan`, leaving its memory uninitialized. -For example: +The names we propose establish a nomenclature for bulk operations that specifies both what happens with the destination and how the source is to be handled. + +The first part of each name is "update", because the functions change values in place in the container. Other related bulk operations include removing elements (`removeAll`, `removeSubrange`) and replacing subranges of elements (`replaceSubrange`)[^2]. These actions should be proposed as part of a range-replaceable container protocol, and we would the methods proposed here to have a family resemblance with them. + +[^2]: These related operations are part of `UniqueArray`'s API ([SE-0527][SE-0527]). + +#### Copying from a `Span` ```swift var destination = array.mutableSpan -destination.updateAll(repeating: 0) -destination.updateSubrange(2..<6, repeating: -1) destination.updateAll(copying: source.span) -destination.updateSubrange(2..<6, copying: source.span) +destination.updateSubrange(2..<6, copying: source.span.extracting(first: 4)) ``` `updateAll` and `updateSubrange` require the source count to match the destination count or the subrange count, and trap when the counts don't match. #### Copying from an `Iterable` source -`updateElements(from:copying:)` handles the case where the amount of data from the source is not known ahead of time. It takes any [`Iterable`][SE-0516] source, such as `Span`, `InlineArray`, and `UniqueArray`: +`updateElements(from:copying:)` handles cases where the amount of data from a source is not known ahead of time. It takes any [`Iterable`][SE-0516] source, such as `Span`, `InlineArray`, and `UniqueArray`: ```swift var destination = array.mutableSpan @@ -78,7 +84,9 @@ destination.updateElements(from: &index, copying: checksum) precondition(index == destination.count) ``` -The overload used above accepts any `Iterable`, including one whose iteration can throw. The `index` is taken as an `inout` parameter because a thrown error must not leave the destination in an unknown state: when the function returns, either normally or by throwing, `index` is updated to the index after the last element updated. +The overload used above accepts any `Iterable`, including one whose iteration can throw. The `index` is taken as an `inout` parameter because a thrown error must not leave the state of the destination unknown to the caller. When the function returns, either normally or by throwing, `index` is updated to the index after the last element updated. + +The elements are updated starting from the element at the index provided. The preposition "from" is appropriate here, but it is confusable with the label in a similar method of `UnsafeMutableBufferPointer`, where `from:` denotes the source of the copied elements. When using an `Iterable` whose `Failure` is `Never`, a convenience overload of `updateElements` is available. It returns the index after the last element updated: @@ -87,20 +95,22 @@ var end = destination.updateElements(from: 0, copying: header) end = destination.updateElements(from: end, copying: payload) ``` -The overloads that take `some Iterable` copy their source completely, and trap if the span cannot contain every element from the source. The source can be shorter than the remaining space. +The overloads that take a generic `Iterable` argument copy their source completely, and trap if the span cannot contain every element from the source. The source can be shorter than the remaining space. -A third overload mutates a [`BorrowingIteratorProtocol`][SE-0516] source, and stops as soon as either the iterator has provided all its elements or the end of the destination span is reached, whichever comes first. When the function returns (normally or by throwing), the iterator is positioned after the last element it provided to the destination. +A third overload mutates a [`BorrowingIteratorProtocol`][SE-0516] source, and stops as soon as either the iterator has provided all its elements or the end of the destination span is reached, whichever comes first. When the function returns normally, the iterator is positioned after the last element it provided to the destination. ```swift var iterator = source.makeBorrowingIterator() -var i = 0 -first.updateElements(from: &i, copying: &iterator) -if i == first.count { - i = 0 - second.updateElements(from: &i, copying: &iterator) +var index = 0 +first.updateElements(from: &index, copying: &iterator) +if index == first.count { + var index2 = 0 + second.updateElements(from: &index2, copying: &iterator) } ``` +We expect the `updateElements` functions to become part of a new protocol family generalizing the `Collection` protocol family. As such, we propose the same name (`updateElements`) for `MutableRawSpan` as for `MutableSpan`, with an element type of `UInt8`. + #### Moving elements `updateAll(moving:)` and `updateSubrange(_:moving:)` mutate an [`OutputSpan`][SE-0485], and move its elements into the destination. The source `OutputSpan` is empty afterwards, with its memory uninitialized. @@ -108,6 +118,7 @@ if i == first.count { ```swift var destination = storage.mutableSpan source.edit { output in + assert(output.count == 2) destination.updateSubrange(2..<4, moving: &output) } assert(source.isEmpty) @@ -137,7 +148,8 @@ extension MutableSpan { ) mutating func updateSubrange( - _ subrange: some RangeExpression, repeating repeatedValue: consuming Element + _ subrange: some RangeExpression, + repeating repeatedValue: consuming Element ) mutating func updateSubrange( @@ -152,7 +164,7 @@ extension MutableSpan { /// - Parameter source: The elements to copy into this span. mutating func updateAll(copying source: Span) - /// Overwrites the elements within a range of indices by copying + /// Overwrites every element within a range of indices by copying /// the elements of the source. /// /// `source` must have exactly as many elements as `subrange`. @@ -185,7 +197,7 @@ extension MutableSpan where Element: ~Copyable { /// - Parameter source: The elements to move into this span. mutating func updateAll(moving source: inout OutputSpan) - /// Overwrites the elements within a range of indices by moving + /// Overwrites every element within a range of indices by moving /// the elements from the source. /// /// `source` must have exactly as many initialized elements as `subrange`. @@ -201,7 +213,8 @@ extension MutableSpan where Element: ~Copyable { ) mutating func updateSubrange( - _ subrange: some RangeExpression, moving source: inout OutputSpan + _ subrange: some RangeExpression, + moving source: inout OutputSpan ) mutating func updateSubrange( @@ -209,7 +222,6 @@ extension MutableSpan where Element: ~Copyable { ) } -@available(SwiftStdlib 6.4, *) extension MutableSpan { /// Overwrites elements of this span, starting at an index, by copying /// every element of the source. @@ -264,8 +276,8 @@ extension MutableSpan { /// - index: The index at which to start copying. It must be a valid /// index of this span, or equal to its `count`. On return, it is /// updated to the index after the last element updated. - /// - source: An iterator over the elements to copy into this span. On - /// return, it is positioned after the last element copied. + /// - source: An iterator over the elements to copy into this span. + /// On normal return, it is positioned after the last element copied. /// - Throws: Any error thrown while reading from `source`. mutating func updateElements< I: BorrowingIteratorProtocol & ~Escapable & ~Copyable @@ -277,11 +289,10 @@ extension MutableSpan { ### `MutableRawSpan` -The `MutableRawSpan` operations are the same as the typed ones, replacing typed sources with `RawSpan` and `OutputRawSpan`. The `updateElements` overloads use an `Iterable` with an element type of `UInt8`. +The `MutableRawSpan` operations are the same as the typed ones, replacing typed sources with `RawSpan` and `OutputRawSpan`, or an element type of `UInt8`. ```swift extension MutableRawSpan { - /// Overwrites every byte of this span with the given value. /// /// - Parameter repeatedByte: The value to set for every byte. @@ -312,7 +323,7 @@ extension MutableRawSpan { /// - Parameter source: The bytes to copy into this span. mutating func updateAll(copying source: RawSpan) - /// Overwrites the bytes within a range of offsets by copying + /// Overwrites every byte within a range of offsets by copying /// the bytes of the source. /// /// `source` must have exactly as many bytes as `subrange`. @@ -342,7 +353,7 @@ extension MutableRawSpan { /// - Parameter source: The bytes to move into this span. mutating func updateAll(moving source: inout OutputRawSpan) - /// Overwrites the bytes within a range of offsets by moving + /// Overwrites every byte within a range of offsets by moving /// the bytes from the source. /// /// `source` must have exactly as many initialized bytes as `subrange`. @@ -366,9 +377,7 @@ extension MutableRawSpan { ) } -@available(SwiftStdlib 6.4, *) extension MutableRawSpan { - /// Overwrites bytes of this span, starting at a byte offset, by copying /// every byte of the source. /// @@ -422,8 +431,8 @@ extension MutableRawSpan { /// - byteOffset: The offset at which to start copying. It must be a valid /// offset into this span, or equal to its `byteCount`. On return, it /// is updated to the offset after the last byte updated. - /// - source: An iterator over the bytes to copy into this span. On - /// return, it is positioned after the last byte copied. + /// - source: An iterator over the bytes to copy into this span. + /// On normal return, it is positioned after the last byte copied. /// - Throws: Any error thrown while reading from `source`. mutating func updateElements< I: BorrowingIteratorProtocol & ~Escapable & ~Copyable @@ -441,7 +450,7 @@ This proposal is additive and source-compatible with existing code. The additions in this proposal will be implemented without creating additional ABI. -These additions require the existence of `Span` or `Iterable`. On ABI-stable platforms, they have a minimum deployment target that matches the availability of `Span` or `Iterable`. +These additions require the existence of `Span` or `Iterable`. On ABI-stable platforms, they have a minimum deployment target that matches the availability of `Iterable` (`updateElements`) or `Span` (the rest). ## Implications on adoption @@ -449,9 +458,13 @@ The additions described in this proposal require a new version of the Swift stan ## Future directions -#### Bulk initialization for `OutputSpan` +#### Bulk appending for `OutputSpan` + +Bulk append operations for `OutputSpan` and `OutputRawSpan` are similar to the operations proposed here, but we are deferring them to a future proposal. The outcome of this proposal will inform a proposal centered around `OutputSpan` and `OutputRawSpan`. -Bulk append operations for `OutputSpan` and `OutputRawSpan` are similar to the operations proposed here, but we are deferring them to a future proposal. The outcome of this proposal will inform a bulk-initialization proposal. +#### Bulk copying within a `MutableSpan` + +Due to the law of exclusivity, the source and the destination cannot be within the same span. Future improvements could provide a method specifically for this purpose, or provide a safe method to split an existing span into two disjoint accesses. #### Piecewise updating from a container of noncopyable elements @@ -459,29 +472,37 @@ We are proposing `updateElements(from:copying:)` to copy elements from an `Itera #### Generalized container protocols -We need a generalization of the `Collection` family of protocols for noncopyable (and nonescapable) elements. In such a generalized family of protocols, the functions described here would become method requirements for a successor to `MutableCollection`. +We need a generalization of the `Collection` family of protocols for noncopyable (and nonescapable) elements. In such a generalized family of protocols, the functions proposed here would become method requirements for a successor to `MutableCollection`. + +#### Bulk copying from `Collection` and `Sequence` conformers + +It is possible to construct a `BorrowingIteratorAdapter` from an `IteratorProtocol` instance, and use it with the matching overload of `updateElements`. [SE-0516][SE-0516] deferred conforming existing `Sequence` types to `Iterable`. Once they conform, they will work directly with `updateElements(from:copying:)`. ## Alternatives considered #### Using only `update` as the base name -`updateAll(repeating:)` is a synonym for the existing `update(repeating:)`, and we considered using names such as `update(subrange:copying:)` for the methods proposed here. We went with `updateAll` and `updateSubrange` for two reasons. +We considered using names such as `update(subrange:copying:)` for the methods proposed here, but we went with `updateAll` and `updateSubrange` for two reasons. First, there are precedents in `replaceSubrange(_:with:)` and `removeSubrange(_:)` (in `RangeReplaceableCollection`), and `replaceSubrange(_:copying:)` (in [`UniqueArray`][SE-0527]), for situations that require a range parameter. `removeSubrange` is paired with `removeAll`. -Second, the name of `update(repeating:)` came from the equivalent method of `UnsafeMutableBufferPointer` ([SE-0370][SE-0370]). `UnsafeMutableBufferPointer`'s `update` method doesn't need a "subrange" parameter because its slicing syntax is so compact. Unfortunately, that compact slicing syntax does not work with noncopyable containers. Given the requirement to pass a range as the first argument, we propose a naming model similar to `replaceSubrange` and `removeSubrange`. +Second, the name of `update(repeating:)` came from the equivalent method of `UnsafeMutableBufferPointer` ([SE-0370][SE-0370]). `UnsafeMutableBufferPointer`'s `update` method doesn't need a "subrange" parameter because its slicing syntax is very compact. Unfortunately, that compact slicing syntax does not work with noncopyable containers. Given the requirement to pass a range as the first argument, we propose a naming model similar to `replaceSubrange` and `removeSubrange`. #### Deprecating the existing `update(repeating:)` -`update(repeating:)` shipped in Swift 6.2 and now has a synonym in `updateAll(repeating:)`. The extra spelling is harmless, so we choose not to deprecate it. +The new `MutableSpan.updateAll(repeating:)` is a synonym for the existing `MutableSpan.update(repeating:)`. The extra spelling is harmless, so we choose not to deprecate it to avoid unnecessary churn. + +#### Providing `updateSubrange(_:moving:)` for `UnsafeMutableBufferPointer` sources + +We do not propose an overload of `updateSubrange(_:moving:)` that takes an `UnsafeMutableBufferPointer`, because it would add little functionality at the cost of expanding the `@unsafe` API surface. With pre-existing functionality, it is possible to use `MutableSpan.withUnsafeMutableBufferPointer` and call `UnsafeMutableBufferPointer`'s `moveUpdate(fromContentsOf:)` method within the closure passed to it. This proposal adds the ability to do the same operation via an `OutputSpan` that has been created from an `UnsafeMutableBufferPointer`. -#### Closure-based bulk updates +#### Generic overloads for `updateSubrange` -A `withMutableSubrange(_:) { ... }` form would scope the sub-span automatically and avoid needing to insert an explicit `consume` to end accesses. As discussed in [SE-0467][SE-0467], the `Span` family is deliberately avoiding new closure-taking APIs, as they compose poorly with each other and with new language features. +The `updateElements(from:copying:)` methods take a generic `Iterable` or `BorrowingIteratorProtocol` argument because these are the only span-friendly protocols in the standard library. Later, when newer container protocols are added, `updateSubrange(_:copying:)` and `updateSubrange(_:moving:)` could gain generic overloads that admit arguments such as `UniqueArray`. This proposal defines the fundamental forms for `updateSubrange` and `updateAll`, which can be used to build protocol default implementations in the future. #### Returning an iterator from `updateElements(from:copying:)` -`UnsafeMutableBufferPointer.update(from:)` ([SE-0370][SE-0370]) is a close counterpart to `updateElements(from:copying:)`. The return type of the older method is a tuple of an iterator and an index. Returning state in this way is viable in non-throwing situations, but iteration over an `Iterable` can throw, and the state would be lost when an error is thrown. When the thrown type is `Never`, we would like to return the iterator in addition to the index, but we cannot do so at this time because tuples cannot contain nonescapable types. We provide an overload that takes an `inout some BorrowingIteratorProtocol` as a replacement for a tuple-returning function. +`UnsafeMutableBufferPointer.update(from:)` ([SE-0370][SE-0370]) is a close counterpart to `updateElements(from:copying:)`. The return type of the older method is a tuple of an iterator and an index. Returning state in this way is viable in non-throwing situations, but iteration over an `Iterable` can throw, and the state would be lost when an error is thrown. When the thrown type is `Never`, we would like to return the iterator in addition to the index, but we cannot do so at this time because tuples cannot contain nonescapable types. We provide an overload that takes an `inout` source conforming to `BorrowingIteratorProtocol` as a replacement for a tuple-returning function. ## Acknowledgements From d389077c5d25f4828f4a068020ac4f87bb60aaa8 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 30 Sep 2026 15:34:04 -0700 Subject: [PATCH 3/6] Add clarifications --- proposals/nnnn-mutablespan-bulk-updates.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-mutablespan-bulk-updates.md b/proposals/nnnn-mutablespan-bulk-updates.md index fde4115015..45b27edafb 100644 --- a/proposals/nnnn-mutablespan-bulk-updates.md +++ b/proposals/nnnn-mutablespan-bulk-updates.md @@ -56,9 +56,11 @@ The source argument of each method can have three different labels, depending on The names we propose establish a nomenclature for bulk operations that specifies both what happens with the destination and how the source is to be handled. -The first part of each name is "update", because the functions change values in place in the container. Other related bulk operations include removing elements (`removeAll`, `removeSubrange`) and replacing subranges of elements (`replaceSubrange`)[^2]. These actions should be proposed as part of a range-replaceable container protocol, and we would the methods proposed here to have a family resemblance with them. +The first part of each of the names proposed here is "update", because the functions change values in place in the container. Other related bulk operations include removing elements (`removeAll`, `removeSubrange`) and replacing subranges of elements (`replaceSubrange`)[^2]. These actions, as well as `insert` and `append`, should be proposed as part of a range-replaceable container protocol, and we would like the methods proposed here to have a family resemblance with them. -[^2]: These related operations are part of `UniqueArray`'s API ([SE-0527][SE-0527]). +The label for the source, either `copying:` or `moving:`, will also apply to all of the above base function names. We expect a type like `UniqueArray` to be usable with both `copying:` and `moving:` overloads, providing control over runtime overhead in ways the `Collection` protocols cannot provide. + +[^2]: The related operations `remove`, `replace`, `insert` and `append` are part of `UniqueArray`'s API ([SE-0527][SE-0527]). #### Copying from a `Span` @@ -161,6 +163,9 @@ extension MutableSpan { /// /// `source` must have exactly as many elements as this span. /// + /// - Note: When `Element: BitwiseCopyable`, this function lowers + /// to LLVM's `memcpy` primitive. + /// /// - Parameter source: The elements to copy into this span. mutating func updateAll(copying source: Span) From dc37f273e38b2a1577182daf18841c20d176562d Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Wed, 30 Sep 2026 17:48:55 -0700 Subject: [PATCH 4/6] Added link to pitch thread --- proposals/nnnn-mutablespan-bulk-updates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-mutablespan-bulk-updates.md b/proposals/nnnn-mutablespan-bulk-updates.md index 45b27edafb..7dc304e1ca 100644 --- a/proposals/nnnn-mutablespan-bulk-updates.md +++ b/proposals/nnnn-mutablespan-bulk-updates.md @@ -7,7 +7,7 @@ * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Implementation: [swiftlang/swift#92466](https://github.com/swiftlang/swift/pull/92466) * Previous Proposal: [SE-0467](0467-MutableSpan.md) -* Review: ([pitch](https://forums.swift.org/...)) +* Review: ([pitch](https://forums.swift.org/t/89843)) [SE-0370]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0370-pointer-family-initialization-improvements.md [SE-0467]: https://github.com/swiftlang/swift-evolution/blob/main/proposals/0467-MutableSpan.md From 327e017e6e2f68109e25aa7fa2997f78545536a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joe=20Groff=F3=A0=84=B1=F3=A0=84=BE=F3=A0=85=84?= =?UTF-8?q?=F3=A0=84=B8=F3=A0=85=82=F3=A0=84=BF=F3=A0=85=80=F3=A0=84=B9?= =?UTF-8?q?=F3=A0=84=B3=F3=A0=85=8F=F3=A0=84=BD=F3=A0=84=B1=F3=A0=84=B7?= =?UTF-8?q?=F3=A0=84=B9=F3=A0=84=B3=F3=A0=85=8F=F3=A0=85=83=F3=A0=85=84?= =?UTF-8?q?=F3=A0=85=82=F3=A0=84=B9=F3=A0=84=BE=F3=A0=84=B7=F3=A0=85=8F?= =?UTF-8?q?=F3=A0=85=84=F3=A0=85=82=F3=A0=84=B9=F3=A0=84=B7=F3=A0=84=B7?= =?UTF-8?q?=F3=A0=84=B5=F3=A0=85=82=F3=A0=85=8F=F3=A0=85=82=F3=A0=84=B5?= =?UTF-8?q?=F3=A0=84=B6=F3=A0=85=85=F3=A0=85=83=F3=A0=84=B1=F3=A0=84=BC?= =?UTF-8?q?=F3=A0=85=8F=F3=A0=84=A1=F3=A0=84=B6=F3=A0=84=B1=F3=A0=84=B5?= =?UTF-8?q?=F3=A0=84=B6=F3=A0=84=B2=F3=A0=84=A6=F3=A0=84=A1=F3=A0=84=A7?= =?UTF-8?q?=F3=A0=84=A7=F3=A0=84=B2=F3=A0=84=A4=F3=A0=84=A6=F3=A0=84=A7?= =?UTF-8?q?=F3=A0=84=A2=F3=A0=84=B4=F3=A0=84=B5=F3=A0=84=B5=F3=A0=84=A0?= =?UTF-8?q?=F3=A0=84=A7=F3=A0=84=B6=F3=A0=84=A9=F3=A0=84=B4=F3=A0=84=A3?= =?UTF-8?q?=F3=A0=84=B1=F3=A0=84=B6=F3=A0=84=B3=F3=A0=84=A6=F3=A0=84=A2?= =?UTF-8?q?=F3=A0=84=A5=F3=A0=84=A8=F3=A0=84=A8=F3=A0=84=B3=F3=A0=84=B3?= =?UTF-8?q?=F3=A0=84=B4=F3=A0=84=A2=F3=A0=84=A6=F3=A0=84=A3=F3=A0=84=A1?= =?UTF-8?q?=F3=A0=84=B5=F3=A0=84=B4=F3=A0=84=B3=F3=A0=84=B6=F3=A0=84=A2?= =?UTF-8?q?=F3=A0=84=A2=F3=A0=84=B5=F3=A0=84=A8=F3=A0=84=B3=F3=A0=84=B3?= =?UTF-8?q?=F3=A0=84=B3=F3=A0=84=A1=F3=A0=84=B6=F3=A0=84=B2=F3=A0=84=A3?= =?UTF-8?q?=F3=A0=84=A5=F3=A0=84=B2=F3=A0=84=A5=F3=A0=84=A0=F3=A0=84=A1?= =?UTF-8?q?=F3=A0=84=B3=F3=A0=84=A9=F3=A0=84=B3=F3=A0=84=A8=F3=A0=84=A6?= Date: Tue, 13 Oct 2026 18:14:38 -0700 Subject: [PATCH 5/6] Canonize SE-0558 and begin review --- ...pan-bulk-updates.md => 0558-mutablespan-bulk-updates.md} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename proposals/{nnnn-mutablespan-bulk-updates.md => 0558-mutablespan-bulk-updates.md} (99%) diff --git a/proposals/nnnn-mutablespan-bulk-updates.md b/proposals/0558-mutablespan-bulk-updates.md similarity index 99% rename from proposals/nnnn-mutablespan-bulk-updates.md rename to proposals/0558-mutablespan-bulk-updates.md index 7dc304e1ca..6ddbcfef40 100644 --- a/proposals/nnnn-mutablespan-bulk-updates.md +++ b/proposals/0558-mutablespan-bulk-updates.md @@ -1,9 +1,9 @@ # Bulk update operations for `MutableSpan` and `MutableRawSpan` -* Proposal: [SE-NNNN](nnnn-mutablespan-bulk-updates.md) +* Proposal: [SE-0558](nnnn-mutablespan-bulk-updates.md) * Author: [Guillaume Lessard](https://github.com/glessard) -* Review Manager: TBD -* Status: **Awaiting review** +* Review Manager: [Joe Groff](https://github.com/jckarter) +* Status: **Active review (October 13...28, 2026)** * Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211) * Implementation: [swiftlang/swift#92466](https://github.com/swiftlang/swift/pull/92466) * Previous Proposal: [SE-0467](0467-MutableSpan.md) From 65f348e2d32249708822d190beaf5425c455a5eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joe=20Groff=F3=A0=84=B1=F3=A0=84=BE=F3=A0=85=84?= =?UTF-8?q?=F3=A0=84=B8=F3=A0=85=82=F3=A0=84=BF=F3=A0=85=80=F3=A0=84=B9?= =?UTF-8?q?=F3=A0=84=B3=F3=A0=85=8F=F3=A0=84=BD=F3=A0=84=B1=F3=A0=84=B7?= =?UTF-8?q?=F3=A0=84=B9=F3=A0=84=B3=F3=A0=85=8F=F3=A0=85=83=F3=A0=85=84?= =?UTF-8?q?=F3=A0=85=82=F3=A0=84=B9=F3=A0=84=BE=F3=A0=84=B7=F3=A0=85=8F?= =?UTF-8?q?=F3=A0=85=84=F3=A0=85=82=F3=A0=84=B9=F3=A0=84=B7=F3=A0=84=B7?= =?UTF-8?q?=F3=A0=84=B5=F3=A0=85=82=F3=A0=85=8F=F3=A0=85=82=F3=A0=84=B5?= =?UTF-8?q?=F3=A0=84=B6=F3=A0=85=85=F3=A0=85=83=F3=A0=84=B1=F3=A0=84=BC?= =?UTF-8?q?=F3=A0=85=8F=F3=A0=84=A1=F3=A0=84=B6=F3=A0=84=B1=F3=A0=84=B5?= =?UTF-8?q?=F3=A0=84=B6=F3=A0=84=B2=F3=A0=84=A6=F3=A0=84=A1=F3=A0=84=A7?= =?UTF-8?q?=F3=A0=84=A7=F3=A0=84=B2=F3=A0=84=A4=F3=A0=84=A6=F3=A0=84=A7?= =?UTF-8?q?=F3=A0=84=A2=F3=A0=84=B4=F3=A0=84=B5=F3=A0=84=B5=F3=A0=84=A0?= =?UTF-8?q?=F3=A0=84=A7=F3=A0=84=B6=F3=A0=84=A9=F3=A0=84=B4=F3=A0=84=A3?= =?UTF-8?q?=F3=A0=84=B1=F3=A0=84=B6=F3=A0=84=B3=F3=A0=84=A6=F3=A0=84=A2?= =?UTF-8?q?=F3=A0=84=A5=F3=A0=84=A8=F3=A0=84=A8=F3=A0=84=B3=F3=A0=84=B3?= =?UTF-8?q?=F3=A0=84=B4=F3=A0=84=A2=F3=A0=84=A6=F3=A0=84=A3=F3=A0=84=A1?= =?UTF-8?q?=F3=A0=84=B5=F3=A0=84=B4=F3=A0=84=B3=F3=A0=84=B6=F3=A0=84=A2?= =?UTF-8?q?=F3=A0=84=A2=F3=A0=84=B5=F3=A0=84=A8=F3=A0=84=B3=F3=A0=84=B3?= =?UTF-8?q?=F3=A0=84=B3=F3=A0=84=A1=F3=A0=84=B6=F3=A0=84=B2=F3=A0=84=A3?= =?UTF-8?q?=F3=A0=84=A5=F3=A0=84=B2=F3=A0=84=A5=F3=A0=84=A0=F3=A0=84=A1?= =?UTF-8?q?=F3=A0=84=B3=F3=A0=84=A9=F3=A0=84=B3=F3=A0=84=A8=F3=A0=84=A6?= Date: Tue, 13 Oct 2026 18:15:21 -0700 Subject: [PATCH 6/6] Fix my typo after renaming --- proposals/0558-mutablespan-bulk-updates.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/0558-mutablespan-bulk-updates.md b/proposals/0558-mutablespan-bulk-updates.md index 6ddbcfef40..bc7b950d53 100644 --- a/proposals/0558-mutablespan-bulk-updates.md +++ b/proposals/0558-mutablespan-bulk-updates.md @@ -1,6 +1,6 @@ # Bulk update operations for `MutableSpan` and `MutableRawSpan` -* Proposal: [SE-0558](nnnn-mutablespan-bulk-updates.md) +* Proposal: [SE-0558](0558-mutablespan-bulk-updates.md) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: [Joe Groff](https://github.com/jckarter) * Status: **Active review (October 13...28, 2026)**