Skip to content

Commit aba979b

Browse files
authored
Merge pull request #2877 from glessard/extracting
[pitch] Apply the `extracting()` slicing pattern to `Span` & `RawSpan`
2 parents de17bf8 + 47c86c5 commit aba979b

File tree

1 file changed

+146
-0
lines changed

1 file changed

+146
-0
lines changed

proposals/0488-extracting.md

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
# Apply the extracting() slicing pattern more widely
2+
3+
* Proposal: [SE-0488](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0488-extracting.md)
4+
* Author: [Guillaume Lessard](https://github.com/glessard)
5+
* Review Manager: [Tony Allevato](https://github.com/allevato)
6+
* Status: **Active Review (July 2–July 16, 2025)**
7+
* Implementation: underscored `_extracting()` members of `Span` and `RawSpan`, pending elsewhere.
8+
* Review: ([pitch](https://forums.swift.org/t/pitch-apply-the-extracting-slicing-pattern-to-span-and-rawspan/80322))
9+
10+
[SE-0437]: 0437-noncopyable-stdlib-primitives.md
11+
[SE-0447]: 0447-span-access-shared-contiguous-storage.md
12+
[SE-0467]: 0467-MutableSpan.md
13+
[Forum-LifetimeAnnotations]: https://forums.swift.org/t/78638
14+
15+
16+
## Introduction and Motivation
17+
18+
Slicing containers is an important operation, and non-copyable values have introduced a significant change in the spelling of that operation. When we [introduced][SE-0437] non-copyable primitives to the standard library, we allowed slicing `UnsafeBufferPointer` and related types via a family of `extracting()` methods. We expanded upon these when introducing [`MutableSpan`][SE-0467].
19+
20+
Now that we have a [supported spelling][Forum-LifetimeAnnotations] for lifetime dependencies, we propose adding the `extracting()` methods to `Span` and `RawSpan`, as well as members of the `UnsafeBufferPointer` family that were missed in [SE-0437][SE-0437].
21+
22+
23+
## Proposed solution
24+
25+
As previously discussed in [SE-0437][SE-0437], the slicing pattern established by the `Collection` protocol cannot be generalized for either non-copyable elements or non-escapable containers. The solution is a family of functions named `extracting()`, with appropriate argument labels.
26+
27+
The family of `extracting()` methods established by the [`MutableSpan` proposal][SE-0467] is as follows:
28+
```swift
29+
public func extracting(_ bounds: Range<Index>) -> Self
30+
public func extracting(_ bounds: some RangeExpression<Index>) -> Self
31+
public func extracting(_: UnboundedRange) -> Self
32+
@unsafe public func extracting(unchecked bounds: Range<Index>) -> Self
33+
@unsafe public func extracting(unchecked bounds: ClosedRange<Index>) -> Self
34+
35+
public func extracting(first maxLength: Int) -> Self
36+
public func extracting(droppingLast k: Int) -> Self
37+
public func extracting(last maxLength: Int) -> Self
38+
public func extracting(droppingFirst k: Int) -> Self
39+
```
40+
41+
These will be provided for the following standard library types:
42+
```swift
43+
Span<T>
44+
RawSpan
45+
UnsafeBufferPointer<T>
46+
UnsafeMutableBufferPointer<T>
47+
Slice<UnsafeBufferPointer<T>>
48+
Slice<UnsafeMutableBufferPointer<T>>
49+
UnsafeRawBufferPointer
50+
UnsafeMutableRawBufferPointer
51+
Slice<UnsafeRawBufferPointer>
52+
Slice<UnsafeMutableRawBufferPointer>
53+
```
54+
Some of the types in the list above already have a subset of the `extracting()` functions; their support will be rounded out to the full set.
55+
56+
57+
## Detailed design
58+
59+
The general declarations for these functions is as follows:
60+
```swift
61+
/// Returns an extracted slice over the items within
62+
/// the supplied range of positions.
63+
///
64+
/// Traps if any position within the range is invalid.
65+
@_lifetime(copy self)
66+
public func extracting(_ bounds: Range<Index>) -> Self
67+
68+
/// Returns an extracted slice over the items within
69+
/// the supplied range of positions.
70+
///
71+
/// Traps if any position within the range is invalid.
72+
@_lifetime(copy self)
73+
public func extracting(_ bounds: some RangeExpression<Index>) -> Self
74+
75+
/// Returns an extracted slice over all items of this container.
76+
@_lifetime(copy self)
77+
public func extracting(_: UnboundedRange) -> Self
78+
79+
/// Returns an extracted slice over the items within
80+
/// the supplied range of positions.
81+
///
82+
/// This function does not validate `bounds`; this is an unsafe operation.
83+
@unsafe @_lifetime(copy self)
84+
public func extracting(unchecked bounds: Range<Index>) -> Self
85+
86+
/// Returns an extracted slice over the items within
87+
/// the supplied range of positions.
88+
///
89+
/// This function does not validate `bounds`; this is an unsafe operation.
90+
@unsafe @_lifetime(copy self)
91+
public func extracting(unchecked bounds: ClosedRange<Index>) -> Self
92+
93+
/// Returns an extracted slice over the initial elements
94+
/// of this container, up to the specified maximum length.
95+
@_lifetime(copy self)
96+
public func extracting(first maxLength: Int) -> Self
97+
98+
/// Returns an extracted slice excluding
99+
/// the given number of trailing elements.
100+
@_lifetime(copy self)
101+
public func extracting(droppingLast k: Int) -> Self
102+
103+
/// Returns an extracted slice containing the final elements
104+
/// of this container, up to the given maximum length.
105+
@_lifetime(copy self)
106+
public func extracting(last maxLength: Int) -> Self
107+
108+
/// Returns an extracted slice excluding
109+
/// the given number of initial elements.
110+
@_lifetime(copy self)
111+
public func extracting(droppingFirst k: Int) -> Self
112+
```
113+
For escapable types, the `@_lifetime` attribute is not applied.
114+
115+
116+
### Usage hints
117+
118+
The `extracting()` pattern, while not completely new, is still a departure over the slice pattern established by the `Collection` protocol. For `Span`, `RawSpan`, `MutableSpan` and `MutableRawSpan`, we can add unavailable subscripts and function with hints towards the corresponding `extracting()` function:
119+
120+
```swift
121+
@available(*, unavailable, renamed: "extracting(_ bounds:)")
122+
public subscript(bounds: Range<Index>) -> Self { extracting(bounds) }
123+
124+
@available(*, unavailable, renamed: "extracting(first:)")
125+
public func droppingFirst(_ k: Int) -> Self { extracting(first: k) }
126+
```
127+
128+
## Source compatibility
129+
This proposal is additive and source-compatible with existing code.
130+
131+
## ABI compatibility
132+
This proposal is additive and ABI-compatible with existing code.
133+
134+
## Implications on adoption
135+
The additions described in this proposal require a new version of the Swift standard library.
136+
137+
## Alternatives considered
138+
This is an extension of an existing pattern. We are not considering a different pattern at this time.
139+
140+
## Future directions
141+
#### Disambiguation over ownership type
142+
The `extracting()` functions proposed here are borrowing. `MutableSpan` has versions defined as mutating, but it could benefit from consuming ones as well. In general there could be a need for all three ownership variants of a given operation (`borrowing`, `consuming`, or `mutating`.) In order to handle these variants, we could establish a pattern for disambiguation by name, or we could invent new syntax to disambiguate by ownership type. This is a complex topic left to future proposals.
143+
144+
## Acknowledgements
145+
Thanks to Karoy Lorentey and Tony Parker.
146+

0 commit comments

Comments
 (0)