Skip to content

Commit 909257e

Browse files
committed
Implement nextElement() on most async sequences that have rethrowing next()
Start implementing `nextElement()` for the various async sequences provided by the concurrency library, starting with those that currently depend on `rethrows` with conformances, an undocumented feature we're trying to stage out of the language. With the exception of `AsyncFlatMapSequence`, all of these async sequences have obvious implementations of `nextElement()`. We'll save that one for later.
1 parent 4109315 commit 909257e

8 files changed

+183
-1
lines changed

stdlib/public/Concurrency/AsyncCompactMapSequence.swift

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,12 @@ extension AsyncCompactMapSequence: AsyncSequence {
8181
/// The compact map sequence produces whatever type of element its
8282
/// transforming closure produces.
8383
public typealias Element = ElementOfResult
84+
/// The type of the error that can be produced by the sequence.
85+
///
86+
/// The compact map sequence produces whatever type of error its
87+
/// base sequence does.
88+
@available(SwiftStdlib 5.11, *)
89+
public typealias Failure = Base.Failure
8490
/// The type of iterator that produces elements of the sequence.
8591
public typealias AsyncIterator = Iterator
8692

@@ -123,6 +129,28 @@ extension AsyncCompactMapSequence: AsyncSequence {
123129
}
124130
}
125131
}
132+
133+
/// Produces the next element in the compact map sequence.
134+
///
135+
/// This iterator calls `next()` on its base iterator; if this call returns
136+
/// `nil`, `next()` returns `nil`. Otherwise, `next()` calls the
137+
/// transforming closure on the received element, returning it if the
138+
/// transform returns a non-`nil` value. If the transform returns `nil`,
139+
/// this method continues to wait for further elements until it gets one
140+
/// that transforms to a non-`nil` value.
141+
@available(SwiftStdlib 5.11, *)
142+
@inlinable
143+
public mutating func nextElement() async throws(Failure) -> ElementOfResult? {
144+
while true {
145+
guard let element = try await baseIterator.nextElement() else {
146+
return nil
147+
}
148+
149+
if let transformed = await transform(element) {
150+
return transformed
151+
}
152+
}
153+
}
126154
}
127155

128156
@inlinable

stdlib/public/Concurrency/AsyncDropFirstSequence.swift

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ extension AsyncDropFirstSequence: AsyncSequence {
7070
/// The drop-first sequence produces whatever type of element its base
7171
/// iterator produces.
7272
public typealias Element = Base.Element
73+
/// The type of errors produced by this asynchronous sequence.
74+
///
75+
/// The drop-first sequence produces whatever type of error its base
76+
/// sequence produces.
77+
@available(SwiftStdlib 5.11, *)
78+
public typealias Failure = Base.Failure
7379
/// The type of iterator that produces elements of the sequence.
7480
public typealias AsyncIterator = Iterator
7581

@@ -108,6 +114,29 @@ extension AsyncDropFirstSequence: AsyncSequence {
108114
count = 0
109115
return try await baseIterator.next()
110116
}
117+
118+
/// Produces the next element in the drop-first sequence.
119+
///
120+
/// Until reaching the number of elements to drop, this iterator calls
121+
/// `nextElement()` on its base iterator and discards the result. If the
122+
/// base iterator returns `nil`, indicating the end of the sequence, this
123+
/// iterator returns `nil`. After reaching the number of elements to drop,
124+
/// this iterator passes along the result of calling `nextElement()` on the
125+
/// base iterator.
126+
@available(SwiftStdlib 5.11, *)
127+
@inlinable
128+
public mutating func nextElement() async throws(Failure) -> Base.Element? {
129+
var remainingToDrop = count
130+
while remainingToDrop > 0 {
131+
guard try await baseIterator.nextElement() != nil else {
132+
count = 0
133+
return nil
134+
}
135+
remainingToDrop -= 1
136+
}
137+
count = 0
138+
return try await baseIterator.nextElement()
139+
}
111140
}
112141

113142
@inlinable

stdlib/public/Concurrency/AsyncDropWhileSequence.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ extension AsyncDropWhileSequence: AsyncSequence {
7979
/// The drop-while sequence produces whatever type of element its base
8080
/// sequence produces.
8181
public typealias Element = Base.Element
82+
/// The type of errors produced by this asynchronous sequence.
83+
///
84+
/// The drop-while sequence produces whatever type of error its base
85+
/// sequence produces.
86+
@available(SwiftStdlib 5.11, *)
87+
public typealias Failure = Base.Failure
8288
/// The type of iterator that produces elements of the sequence.
8389
public typealias AsyncIterator = Iterator
8490

@@ -120,6 +126,30 @@ extension AsyncDropWhileSequence: AsyncSequence {
120126
}
121127
return try await baseIterator.next()
122128
}
129+
130+
/// Produces the next element in the drop-while sequence.
131+
///
132+
/// This iterator calls `nextElement()` on its base iterator and evaluates
133+
/// the result with the `predicate` closure. As long as the predicate
134+
/// returns `true`, this method returns `nil`. After the predicate returns
135+
/// `false`, for a value received from the base iterator, this method
136+
/// returns that value. After that, the iterator returns values received
137+
/// from its base iterator as-is, and never executes the predicate closure
138+
/// again.
139+
@available(SwiftStdlib 5.11, *)
140+
@inlinable
141+
public mutating func nextElement() async throws(Failure) -> Base.Element? {
142+
while let predicate = self.predicate {
143+
guard let element = try await baseIterator.nextElement() else {
144+
return nil
145+
}
146+
if await predicate(element) == false {
147+
self.predicate = nil
148+
return element
149+
}
150+
}
151+
return try await baseIterator.nextElement()
152+
}
123153
}
124154

125155
/// Creates an instance of the drop-while sequence iterator.

stdlib/public/Concurrency/AsyncFilterSequence.swift

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,12 @@ extension AsyncFilterSequence: AsyncSequence {
6969
/// The filter sequence produces whatever type of element its base
7070
/// sequence produces.
7171
public typealias Element = Base.Element
72+
/// The type of the error that can be produced by the sequence.
73+
///
74+
/// The filter sequence produces whatever type of error its
75+
/// base sequence does.
76+
@available(SwiftStdlib 5.11, *)
77+
public typealias Failure = Base.Failure
7278
/// The type of iterator that produces elements of the sequence.
7379
public typealias AsyncIterator = Iterator
7480

@@ -107,6 +113,26 @@ extension AsyncFilterSequence: AsyncSequence {
107113
}
108114
}
109115
}
116+
117+
/// Produces the next element in the filter sequence.
118+
///
119+
/// This iterator calls `nextelement()` on its base iterator; if this call
120+
/// returns `nil`, `nextElement()` returns nil. Otherwise, `nextElement()`
121+
/// evaluates the result with the `predicate` closure. If the closure
122+
/// returns `true`, `nextElement()` returns the received element; otherwise
123+
/// it awaits the next element from the base iterator.
124+
@available(SwiftStdlib 5.11, *)
125+
@inlinable
126+
public mutating func nextElement() async throws(Failure) -> Base.Element? {
127+
while true {
128+
guard let element = try await baseIterator.nextElement() else {
129+
return nil
130+
}
131+
if await isIncluded(element) {
132+
return element
133+
}
134+
}
135+
}
110136
}
111137

112138
@inlinable

stdlib/public/Concurrency/AsyncIteratorProtocol.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ public protocol AsyncIteratorProtocol<Element, Failure> {
112112
extension AsyncIteratorProtocol {
113113
/// Default implementation of `nextElement()` in terms of `next()`, which is
114114
/// required to maintain backward compatibility with existing async iterators.
115-
@_alwaysEmitIntoClient
115+
@available(SwiftStdlib 5.11, *)
116+
@inlinable
116117
public mutating func nextElement() async throws(Failure) -> Element? {
117118
do {
118119
return try await next()

stdlib/public/Concurrency/AsyncMapSequence.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,12 @@ extension AsyncMapSequence: AsyncSequence {
7979
/// The map sequence produces whatever type of element its transforming
8080
/// closure produces.
8181
public typealias Element = Transformed
82+
/// The type of the error that can be produced by the sequence.
83+
///
84+
/// The map sequence produces whatever type of error its
85+
/// base sequence does.
86+
@available(SwiftStdlib 5.11, *)
87+
public typealias Failure = Base.Failure
8288
/// The type of iterator that produces elements of the sequence.
8389
public typealias AsyncIterator = Iterator
8490

@@ -111,6 +117,20 @@ extension AsyncMapSequence: AsyncSequence {
111117
}
112118
return await transform(element)
113119
}
120+
121+
/// Produces the next element in the map sequence.
122+
///
123+
/// This iterator calls `next()` on its base iterator; if this call returns
124+
/// `nil`, `next()` returns `nil`. Otherwise, `next()` returns the result of
125+
/// calling the transforming closure on the received element.
126+
@available(SwiftStdlib 5.11, *)
127+
@inlinable
128+
public mutating func nextElement() async throws(Failure) -> Transformed? {
129+
guard let element = try await baseIterator.nextElement() else {
130+
return nil
131+
}
132+
return await transform(element)
133+
}
114134
}
115135

116136
@inlinable

stdlib/public/Concurrency/AsyncPrefixSequence.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ extension AsyncPrefixSequence: AsyncSequence {
7070
/// The prefix sequence produces whatever type of element its base iterator
7171
/// produces.
7272
public typealias Element = Base.Element
73+
/// The type of the error that can be produced by the sequence.
74+
///
75+
/// The prefix sequence produces whatever type of error its
76+
/// base sequence does.
77+
@available(SwiftStdlib 5.11, *)
78+
public typealias Failure = Base.Failure
7379
/// The type of iterator that produces elements of the sequence.
7480
public typealias AsyncIterator = Iterator
7581

@@ -102,6 +108,23 @@ extension AsyncPrefixSequence: AsyncSequence {
102108
return nil
103109
}
104110
}
111+
112+
/// Produces the next element in the prefix sequence.
113+
///
114+
/// Until reaching the number of elements to include, this iterator calls
115+
/// `nextElement()` on its base iterator and passes through the
116+
/// result. After reaching the maximum number of elements, subsequent calls
117+
/// to `nextElement()` return `nil`.
118+
@available(SwiftStdlib 5.11, *)
119+
@inlinable
120+
public mutating func nextElement() async throws(Failure) -> Base.Element? {
121+
if remaining != 0 {
122+
remaining &-= 1
123+
return try await baseIterator.nextElement()
124+
} else {
125+
return nil
126+
}
127+
}
105128
}
106129

107130
@inlinable

stdlib/public/Concurrency/AsyncPrefixWhileSequence.swift

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,12 @@ extension AsyncPrefixWhileSequence: AsyncSequence {
7474
/// The prefix-while sequence produces whatever type of element its base
7575
/// iterator produces.
7676
public typealias Element = Base.Element
77+
/// The type of the error that can be produced by the sequence.
78+
///
79+
/// The prefix-while sequence produces whatever type of error its
80+
/// base sequence does.
81+
@available(SwiftStdlib 5.11, *)
82+
public typealias Failure = Base.Failure
7783
/// The type of iterator that produces elements of the sequence.
7884
public typealias AsyncIterator = Iterator
7985

@@ -114,6 +120,25 @@ extension AsyncPrefixWhileSequence: AsyncSequence {
114120
}
115121
return nil
116122
}
123+
124+
/// Produces the next element in the prefix-while sequence.
125+
///
126+
/// If the predicate hasn't yet failed, this method gets the next element
127+
/// from the base sequence and calls the predicate with it. If this call
128+
/// succeeds, this method passes along the element. Otherwise, it returns
129+
/// `nil`, ending the sequence.
130+
@available(SwiftStdlib 5.11, *)
131+
@inlinable
132+
public mutating func nextElement() async throws(Failure) -> Base.Element? {
133+
if !predicateHasFailed, let nextElement = try await baseIterator.nextElement() {
134+
if await predicate(nextElement) {
135+
return nextElement
136+
} else {
137+
predicateHasFailed = true
138+
}
139+
}
140+
return nil
141+
}
117142
}
118143

119144
@inlinable

0 commit comments

Comments
 (0)