diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md new file mode 100644 index 0000000000..e488808f98 --- /dev/null +++ b/proposals/nnnn-extracting.md @@ -0,0 +1,146 @@ +# Apply the extracting() slicing pattern more widely + +* Proposal: [TBD](https://github.com/swiftlang/swift-evolution/pull/2877) +* Author: [Guillaume Lessard](https://github.com/glessard) +* Review Manager: TBD +* Status: **Pitch** +* Implementation: underscored `_extracting()` members of `Span` and `RawSpan`, pending elsewhere. +* Review: Pending + +[SE-0437]: 0437-noncopyable-stdlib-primitives.md +[SE-0447]: 0447-span-access-shared-contiguous-storage.md +[SE-0467]: 0467-MutableSpan.md +[Forum-LifetimeAnnotations]: https://forums.swift.org/t/78638 + + +## Introduction and Motivation + +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]. + +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]. + + +## Proposed solution + +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. + +The family of `extracting()` methods established by the [`MutableSpan` proposal][SE-0467] is as follows: +```swift +public func extracting(_ bounds: Range) -> Self +public func extracting(_ bounds: some RangeExpression) -> Self +public func extracting(_: UnboundedRange) -> Self +@unsafe public func extracting(unchecked bounds: Range) -> Self +@unsafe public func extracting(unchecked bounds: ClosedRange) -> Self + +public func extracting(first maxLength: Int) -> Self +public func extracting(droppingLast k: Int) -> Self +public func extracting(last maxLength: Int) -> Self +public func extracting(droppingFirst k: Int) -> Self +``` + +These will be provided for the following standard library types: +```swift +Span +RawSpan +UnsafeBufferPointer +UnsafeMutableBufferPointer +Slice> +Slice> +UnsafeRawBufferPointer +UnsafeMutableRawBufferPointer +Slice +Slice +``` +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. + + +## Detailed design + +The general declarations for these functions is as follows: +```swift +/// Returns an extracted slice over the items within +/// the supplied range of positions. +/// +/// Traps if any position within the range is invalid. +@_lifetime(copy self) +public func extracting(_ bounds: Range) -> Self + +/// Returns an extracted slice over the items within +/// the supplied range of positions. +/// +/// Traps if any position within the range is invalid. +@_lifetime(copy self) +public func extracting(_ bounds: some RangeExpression) -> Self + +/// Returns an extracted slice over all items of this container. +@_lifetime(copy self) +public func extracting(_: UnboundedRange) -> Self + +/// Returns an extracted slice over the items within +/// the supplied range of positions. +/// +/// This function does not validate `bounds`; this is an unsafe operation. +@unsafe @_lifetime(copy self) +public func extracting(unchecked bounds: Range) -> Self + +/// Returns an extracted slice over the items within +/// the supplied range of positions. +/// +/// This function does not validate `bounds`; this is an unsafe operation. +@unsafe @_lifetime(copy self) +public func extracting(unchecked bounds: ClosedRange) -> Self + +/// Returns an extracted slice over the initial elements +/// of this container, up to the specified maximum length. +@_lifetime(copy self) +public func extracting(first maxLength: Int) -> Self + +/// Returns an extracted slice excluding +/// the given number of trailing elements. +@_lifetime(copy self) +public func extracting(droppingLast k: Int) -> Self + +/// Returns an extracted slice containing the final elements +/// of this container, up to the given maximum length. +@_lifetime(copy self) +public func extracting(last maxLength: Int) -> Self + +/// Returns an extracted slice excluding +/// the given number of initial elements. +@_lifetime(copy self) +public func extracting(droppingFirst k: Int) -> Self +``` +For escapable types, the `@_lifetime` attribute is not applied. + + +### Usage hints + +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: + +```swift +@available(*, unavailable, renamed: "extracting(_ bounds:)") +public subscript(bounds: Range) -> Self { extracting(bounds) } + +@available(*, unavailable, renamed: "extracting(first:)") +public func droppingFirst(_ k: Int) -> Self { extracting(first: k) } +``` + +## Source compatibility +This proposal is additive and source-compatible with existing code. + +## ABI compatibility +This proposal is additive and ABI-compatible with existing code. + +## Implications on adoption +The additions described in this proposal require a new version of the Swift standard library. + +## Alternatives considered +This is an extension of an existing pattern. We are not considering a different pattern at this time. + +## Future directions +#### Disambiguation over ownership type +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. + +## Acknowledgements +Thanks to Karoy Lorentey and Tony Parker. +