From d6f8cd455e20d82dc29f817ac8129e8364516cd6 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Tue, 3 Jun 2025 17:56:53 -0700 Subject: [PATCH 01/12] initial pitch --- proposals/nnnn-extracting.md | 130 +++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 proposals/nnnn-extracting.md diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md new file mode 100644 index 0000000000..c097cb762b --- /dev/null +++ b/proposals/nnnn-extracting.md @@ -0,0 +1,130 @@ +# Expand the `extracting()` slicing pattern to more types + +* Proposal: [SE-0485](0485-outputspan.md) +* Author: [Guillaume Lessard](https://github.com/glessard) +* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Status: **Pitch** +* Implementation: underscored `_extracting()` members of `Span` and `RawSpan`, pending elsewhere. +* Review: Pending + +[SE-0437]: proposals/0437-noncopyable-stdlib-primitives.md +[SE-0447]: proposals/0447-span-access-shared-contiguous-storage.md +[SE-0467]: proposals/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 [stable 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 + +The family of `extracting()` methods 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 should 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(_ byteOffsets: 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(_ byteOffsets: 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 non-escapable types, the `@_lifetime` attribute is ignored as being non-applicable. + +## Source compatibility +This proposal is additive and source-copmatible 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 semantically consuming. `MutableSpan` has versions defined as mutations, but it could benefit from consuming ones as well. In order to do this, 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. + From 54565a0f390f8b82b17aefd27c7a1b2686606e3c Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 12:26:07 -0700 Subject: [PATCH 02/12] sanitize copy-pasted information --- proposals/nnnn-extracting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index c097cb762b..b82eb09824 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -1,8 +1,8 @@ # Expand the `extracting()` slicing pattern to more types -* Proposal: [SE-0485](0485-outputspan.md) +* Proposal: TBD * Author: [Guillaume Lessard](https://github.com/glessard) -* Review Manager: [Doug Gregor](https://github.com/DougGregor) +* Review Manager: TBD * Status: **Pitch** * Implementation: underscored `_extracting()` members of `Span` and `RawSpan`, pending elsewhere. * Review: Pending From a4b2002c1e2789cf9d183bb83c80a98f9752ec3c Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 14:04:18 -0700 Subject: [PATCH 03/12] add usage hints --- proposals/nnnn-extracting.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index b82eb09824..e65bb0f56a 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -109,6 +109,18 @@ public func extracting(droppingFirst k: Int) -> Self ``` For non-escapable types, the `@_lifetime` attribute is ignored as being non-applicable. +#### 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-copmatible with existing code. From 38a77e9d017f278381ac947cb177a9e3edbe9730 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 14:04:58 -0700 Subject: [PATCH 04/12] improve opening graf of proposed solution --- proposals/nnnn-extracting.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index e65bb0f56a..4e96d807a7 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -22,7 +22,9 @@ Now that we have a [stable spelling][Forum-LifetimeAnnotations] for lifetime dep ## Proposed solution -The family of `extracting()` methods is as follows: +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 From 0113278618391112020934bc939b7c26ee3a3f91 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 14:05:17 -0700 Subject: [PATCH 05/12] spelling and formatting --- proposals/nnnn-extracting.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index 4e96d807a7..1e42a040c9 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -12,6 +12,7 @@ [SE-0467]: proposals/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]. @@ -19,7 +20,6 @@ Slicing containers is an important operation, and non-copyable values have intro Now that we have a [stable 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. @@ -28,7 +28,7 @@ The family of `extracting()` methods established by the [`MutableSpan` proposal] ```swift public func extracting(_ bounds: Range) -> Self public func extracting(_ bounds: some RangeExpression) -> Self -public func extracting(_ UnboundedRange) -> Self +public func extracting(_: UnboundedRange) -> Self @unsafe public func extracting(unchecked bounds: Range) -> Self @unsafe public func extracting(unchecked bounds: ClosedRange) -> Self @@ -38,7 +38,7 @@ public func extracting(last maxLength: Int) -> Self public func extracting(droppingFirst k: Int) -> Self ``` -These should be provided for the following standard library types: +These will be provided for the following standard library types: ```swift Span RawSpan @@ -53,6 +53,7 @@ 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: @@ -73,7 +74,7 @@ public func extracting(_ byteOffsets: some RangeExpression) -> Self /// Returns an extracted slice over all items of this container. @_lifetime(copy self) -public func extracting(_ UnboundedRange) -> Self +public func extracting(_: UnboundedRange) -> Self /// Returns an extracted slice over the items within /// the supplied range of positions. @@ -109,7 +110,8 @@ public func extracting(last maxLength: Int) -> Self @_lifetime(copy self) public func extracting(droppingFirst k: Int) -> Self ``` -For non-escapable types, the `@_lifetime` attribute is ignored as being non-applicable. +For escapable types, the `@_lifetime` attribute is not applied. + #### Usage hints @@ -124,7 +126,7 @@ public func droppingFirst(_ k: Int) -> Self { extracting(first: k) } ``` ## Source compatibility -This proposal is additive and source-copmatible with existing code. +This proposal is additive and source-compatible with existing code. ## ABI compatibility This proposal is additive and ABI-compatible with existing code. From 495f9be58cde92318ed00889214c6758c99c9367 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 15:16:32 -0700 Subject: [PATCH 06/12] formatting --- proposals/nnnn-extracting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index 1e42a040c9..2c7e0cf4a7 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -113,7 +113,7 @@ public func extracting(droppingFirst k: Int) -> Self For escapable types, the `@_lifetime` attribute is not applied. -#### Usage hints +### 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: From 0184b3044b1d06a839e4bb6f08c6e2686575b8b8 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 15:16:43 -0700 Subject: [PATCH 07/12] clarification --- proposals/nnnn-extracting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index 2c7e0cf4a7..f455481523 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -139,7 +139,7 @@ This is an extension of an existing pattern. We are not considering a different ## Future directions #### Disambiguation over ownership type -The `extracting()` functions proposed here are semantically consuming. `MutableSpan` has versions defined as mutations, but it could benefit from consuming ones as well. In order to do this, 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. +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. From 9a1371e1d4af3a9bba5ce4209f73ec6cdeece22a Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 15:39:28 -0700 Subject: [PATCH 08/12] tweak title --- proposals/nnnn-extracting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index f455481523..059a458316 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -1,4 +1,4 @@ -# Expand the `extracting()` slicing pattern to more types +# Apply the extracting() slicing pattern more widely * Proposal: TBD * Author: [Guillaume Lessard](https://github.com/glessard) From cc360ddf41d12401a85109f23216f3c965b4e8ed Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 15:41:35 -0700 Subject: [PATCH 09/12] link to the proposal pull request --- proposals/nnnn-extracting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index 059a458316..27c39171ae 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -1,6 +1,6 @@ # Apply the extracting() slicing pattern more widely -* Proposal: TBD +* Proposal: [TBD](https://github.com/swiftlang/swift-evolution/pull/2877) * Author: [Guillaume Lessard](https://github.com/glessard) * Review Manager: TBD * Status: **Pitch** From fa3e8a4fcb8ff271853e38a0e3dafc7d0f15f348 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 15:51:08 -0700 Subject: [PATCH 10/12] fix from review comments --- proposals/nnnn-extracting.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index 27c39171ae..096fb65d36 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -63,14 +63,14 @@ The general declarations for these functions is as follows: /// /// Traps if any position within the range is invalid. @_lifetime(copy self) -public func extracting(_ byteOffsets: Range) -> 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(_ byteOffsets: some RangeExpression) -> Self +public func extracting(_ bounds: some RangeExpression) -> Self /// Returns an extracted slice over all items of this container. @_lifetime(copy self) From 19c78ae5b5c46a48d2a2827bc90f10ad9292f808 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Thu, 5 Jun 2025 16:01:50 -0700 Subject: [PATCH 11/12] nit-picky wording change --- proposals/nnnn-extracting.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index 096fb65d36..2bfcc556d7 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -17,7 +17,7 @@ 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 [stable 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]. +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 From ba32f1a22e0e3deae2797784764640403a4dcfe8 Mon Sep 17 00:00:00 2001 From: Guillaume Lessard Date: Mon, 9 Jun 2025 07:37:58 -0700 Subject: [PATCH 12/12] Update proposals/nnnn-extracting.md Co-authored-by: Ben Rimmington --- proposals/nnnn-extracting.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/proposals/nnnn-extracting.md b/proposals/nnnn-extracting.md index 2bfcc556d7..e488808f98 100644 --- a/proposals/nnnn-extracting.md +++ b/proposals/nnnn-extracting.md @@ -7,9 +7,9 @@ * Implementation: underscored `_extracting()` members of `Span` and `RawSpan`, pending elsewhere. * Review: Pending -[SE-0437]: proposals/0437-noncopyable-stdlib-primitives.md -[SE-0447]: proposals/0447-span-access-shared-contiguous-storage.md -[SE-0467]: proposals/0467-MutableSpan.md +[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