You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: proposals/nnnn-MutableSpan.md
+37-17Lines changed: 37 additions & 17 deletions
Original file line number
Diff line number
Diff line change
@@ -29,13 +29,13 @@ These functions have a few different drawbacks, most prominently their reliance
29
29
In addition to the new types, we will propose adding new API some standard library types to take advantage of `MutableSpan` and `MutableRawSpan`.
30
30
31
31
## Proposed solution
32
-
We previously introduced `Span` to provide shared read-only access to containers. A question can be raised as to whether this same type could be used for mutations. We cannot, due to the [law of exclusivity][SE-0176]. `Span` is copyable, and it should be copyable in order to properly model read access under the law of exclusivity: a value can be simultaneously accessed through multiple read-only accesses. Mutations, on the other hand, require _exclusive access_. Exclusive access cannot be modeled through a copyable type, since a copy of the value representing the access would violate exclusivity. We therefore need a type separate from `Span` in order to model mutations.
32
+
We previously introduced `Span` to provide shared read-only access to containers. We cannot use `Span`to also model container mutations, due to the [law of exclusivity][SE-0176]. `Span` is copyable, and must be copyable in order to properly model read access under the law of exclusivity: a value can be simultaneously accessed through multiple read-only accesses. Mutations, on the other hand, require _exclusive access_. Exclusive access cannot be modeled through a copyable type, since a copy of the value representing the access would by definition violate exclusivity. We therefore need a non-copyable type separate from `Span` in order to model mutations.
33
33
34
34
#### MutableSpan
35
35
36
-
`MutableSpan` allows delegating mutations of a type's contiguous internal representation, by providing access to an exclusively-borrowed view of a range of contiguous, initialized memory. `MutableSpan` relies on guarantees that it has exclusive access to the range of memory it represents, and that the memory it represents will remain valid for the duration of the access. These guarantee data race safety and temporal safety. Like `Span`, `MutableSpan` performs bounds-checking on every access to preserve spatial safety.
36
+
`MutableSpan` allows delegating mutations of a type's contiguous internal representation, by providing access to an exclusively-borrowed view of a range of contiguous, initialized memory. `MutableSpan` relies on guarantees that it has exclusive access to the range of memory it represents, and that the memory it represents will remain valid for the duration of the access. These provide data race safety and temporal safety. Like `Span`, `MutableSpan` performs bounds-checking on every access to preserve spatial safety.
37
37
38
-
A `MutableSpan` provided by a container represents a mutation of that container, via an exclusive borrow. Mutations are implemented by mutating operations, which let the compiler statically enforce exclusivity.
38
+
A `MutableSpan` provided by a container represents a mutation of that container, via an exclusive borrow. Mutations are implemented by mutating functions and subscripts, which let the compiler statically enforce exclusivity.
These computed properties represent a case of lifetime relationships between two bindings that wasn't covered in [SE-0456][SE-0456]. There we defined lifetime relationships for computed property getters of non-escapable and copyable types (`~Escapable & Copyable`). We now need to define them for properties of non-escapable and non-copyable types (`~Escapable & ~Copyable`). A `~Escapable & ~Copyable` value borrows another binding; if this borrow is also a mutation then it is an exclusive borrow. The scope of the borrow, whether or not it is exclusive, extends until the last use of the dependent binding.
58
+
These computed properties represent a case of lifetime relationships not covered in [SE-0456][SE-0456]. In SE-0456 we defined lifetime relationships for computed property getters of non-escapable and copyable types (`~Escapable & Copyable`). We propose defining them for properties of non-escapable and non-copyable types (`~Escapable & ~Copyable`). A `~Escapable & ~Copyable` value borrows another binding; if this borrow is also a mutation then it is an exclusive borrow. The scope of the borrow, whether or not it is exclusive, extends until the last use of the dependent binding.
We store a `UnsafeMutableRawPointer` value internally in order to explicitly support reinterpreted views of memory as containing different types of `BitwiseCopyable` elements. Note that the the optionality of the pointer does not affect usage of `MutableSpan`, since accesses are bounds-checked and the pointer is only dereferenced when the `MutableSpan` isn't empty, when the pointer cannot be `nil`.
77
77
78
+
Initializers, required for library adoption, will be proposed alongside [lifetime annotations][PR-2305]; for details, see "[Initializers](#initializers)" in the [future directions](#Directions) section.
79
+
78
80
```swift
79
81
extensionMutableSpanwhereElement:~Copyable {
80
82
/// The number of initialized elements in this `MutableSpan`.
@@ -90,10 +92,14 @@ extension MutableSpan where Element: ~Copyable {
90
92
var indices: Range<Index> { get }
91
93
92
94
/// Accesses the element at the specified position.
// accessor syntax from accessors roadmap (https://forums.swift.org/t/76707)
94
97
95
98
/// Exchange the elements at the two given offsets
96
99
mutatingfuncswapAt(_i: Index, _j: Index)
100
+
101
+
/// Borrow the underlying memory for read-only access
102
+
var span: Span<Element> { borrowingget }
97
103
}
98
104
```
99
105
@@ -111,25 +117,34 @@ for i in myMutableSpan.indices {
111
117
}
112
118
```
113
119
114
-
##### `MutableSpan` API:
120
+
##### Unchecked access to elements:
115
121
116
-
Initializers, required for library adoption, will be proposed alongside [lifetime annotations][PR-2305]; for details, see "[Initializers](#initializers)" in the [future directions](#Directions) section.
122
+
The `subscript` mentioned above always checks the bounds of the `MutableSpan` before allowing access to the memory, preventing out-of-bounds accesses. We also provide an unchecked variant of the `subscript` and of the `swapAt` function as an alternative for situations where bounds-checking is costly and has already been performed:
117
123
118
124
```swift
119
125
extensionMutableSpanwhereElement:~Copyable {
126
+
/// Accesses the element at the specified `position`.
127
+
///
128
+
/// This subscript does not validate `position`; this is an unsafe operation.
129
+
///
130
+
/// - Parameter position: The offset of the element to access. `position`
131
+
/// must be greater or equal to zero, and less than `count`.
##### Bulk updating of a `MutableSpan`'s elements:
130
143
131
144
We include functions to perform bulk copies into the memory represented by a `MutableSpan`. Updating a `MutableSpan` from known-sized sources (such as `Collection` or `Span`) copies every element of a source. It is an error to do so when there is the span is too short to contain every element from the source. Updating a `MutableSpan` from `Sequence` or `IteratorProtocol` instances will copy as many items as possible, either until the input is empty or until the operation has updated the item at the last index.
132
145
146
+
**Note:** This set of functions is sufficiently complete in functionality, but this minimal approach to slicing is only one of many possible approaches. We could revive the option of using a `some RangeExpression` parameter, or we could use the return value of a `func extracting(_: some RangeExpression)` such as was recently added to `UnsafeBufferPointer` in support of non-copyable elements. The latter option in combination with `mutating` functions requires the use of intermediate bindings.
@@ -203,7 +219,7 @@ These functions use a closure to define the scope of validity of `buffer`, ensur
203
219
204
220
#### MutableRawSpan
205
221
206
-
`MutableRawSpan` is similar to `MutableSpan<T>`, but reperesents untyped initialized bytes. `MutableRawSpan` specifically supports encoding and decoding applications. Its API supports `unsafeLoad(as:)` and `storeBytes(of: as:)`, as well as a variety of bulk copying operations.
222
+
`MutableRawSpan` is similar to `MutableSpan<T>`, but represents untyped initialized bytes. `MutableRawSpan` specifically supports encoding and decoding applications. Its API supports `unsafeLoad(as:)` and `storeBytes(of: as:)`, as well as a variety of bulk copying operations.
207
223
208
224
##### `MutableRawSpan` API:
209
225
@@ -265,12 +281,12 @@ To these, `MutableRawSpan` adds functions to store the bytes of a `BitwiseCopyab
The `mutableSpan` and `mutableBytes` properties should be performant and return their `MutableSpan` or `MutableRawSpan` with very little work, in O(1) time. In copy-on-write types, however, obtaining a `MutableSpan` is the start of the mutation, and if the backing buffer is not uniquely reference a copy must be made ahead of returning the `MutableSpan`.
414
430
415
-
Note that `MutableSpan` incurs no special behaviour for bridged types, since mutations always require a defensive copy of data bridged from Objective-C data structures.
431
+
Note that `MutableSpan` incurs no special behaviour for bridged types, since mutable bindings always require a defensive copy of data bridged from Objective-C data structures.
416
432
417
433
## Source compatibility
418
434
@@ -478,7 +494,7 @@ extension MutableRawSpan {
478
494
```
479
495
We are subsetting functions that require lifetime annotations until such annotations are [proposed][PR-2305].
480
496
481
-
#### Splitting `MutableSpan` instances
497
+
#### Splitting `MutableSpan` instances – `MutableSpan` in divide-and-conquer algorithms
482
498
483
499
It is desirable to have a way to split a `MutableSpan` in multiple parts, for divide-and-conquer algorithms or other reasons:
484
500
@@ -488,7 +504,11 @@ extension MutableSpan where Element: ~Copyable {
488
504
}
489
505
```
490
506
491
-
Unfortunately, tuples do not support non-copyable values yet. We may be able to use the new `Vector`/`Slab`/`InlineArray` type proposed in [SE-0453][SE-0453], but destructuring its non-copyable elements remains a challenge. Solving this issue for `Span` as well as `MutableSpan` is a top priority.
507
+
Unfortunately, tuples do not support non-copyable values yet. We may be able to use `InlineArray` ([SE-0453][SE-0453]), or a bespoke type, but destructuring the non-copyable constituent part remains a challenge. Solving this issue for `Span` as well as `MutableSpan` is a top priority.
508
+
509
+
#### Mutating algorithms
510
+
511
+
Algorithms defined on `MutableCollection` such as `sort(by:)` and `partition(by:)` could be defined on `MutableSpan`. We believe we will be able to define these more generally once we have a generalized container protocol hierarchy.
492
512
493
513
#### <aname="OutputSpan"></a>Delegated initialization with `OutputSpan<T>`
0 commit comments