# \[Pitch\] Borrowing Sequence

**URL:** <https://forums.swift.org/t/pitch-borrowing-sequence/84332>\
**Category:** Pitches\
**Created:** [January 24, 2026, 3:15am UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332 "2026-01-24T03:15:56Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ben-cohen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ben-cohen/32/296_2.png) [@ben-cohen](https://forums.swift.org/u/ben-cohen)\
**Post date:** [January 24, 2026, 3:15am UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/1 "2026-01-24T03:15:57Z")

</div>

Hi Swift Evolution.

Another std lib noncopyable adoption pitch for you.

**Note** this is the foundational proposal, which is expected to be reviewed in conjunction with some satellite proposals – in particular, the current pitch to be able to reparent protocols, and a proposal that uses that feature to reparent `Sequence` on top of this proposed protocol. More to come on this topic soon, but hopefully this can get the ball rolling on the core functionality being proposed.

The proposal PR can be found [here](https://github.com/swiftlang/swift-evolution/pull/3093/), please direct spelling/grammar/typos there.

# Borrowing Sequence

- Proposal: SE-NNNN
- Authors: [Nate Cook](https://github.com/natecook1000), [Ben Cohen](https://github.com/airspeedswift)
- Review Manager: TBD
- Status: **Awaiting review**
- Implementation: [swiftlang/swift#86066](https://github.com/swiftlang/swift/pull/86066)
- Upcoming Feature Flag: TBD
- Review: ([pitch](https://forums.swift.org/...))

## Introduction

We propose a new protocol for iteration, `BorrowingSequence`, which  
will work with noncopyable types and provide more efficient iteration  
in some circumstances for copyable types. The Swift compiler will  
support use of this protocol via the familiar `for...in` syntax.

## Motivation

The `Sequence` protocol first appeared alongside collections like `Array` in the early  
days of Swift, and has many appealing characteristics, allowing iteration and  
generic algorithms on sequences based on the simple `for`-`in` primitive.

However, it predates the introduction of `~Copyable` and `~Escapable`, and is fundamentally based  
around copyable elements, posing a limitation on working with `Span` types, inline arrays, and  
other types for collections of noncopyable elements.

Protocols can have requirements loosened on them, such as with the introduction  
of `~Copyable` to `Equatable` in [SE-0499](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0499-support-non-copyable-simple-protocols.md),  
or using the proposed  
[SE-0503](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0503-suppressed-associated-types.md),  
which would provide language support for associated types to be marked `~Copyable`  
or `~Escapable`.

The design challenge with `Sequence`, though, is more fundamental.  
A sequence provides access to its elements through an `Iterator`,  
and an iterator's `next()` operation _returns_ an `Element?`.  
For a sequence of noncopyable elements, this operation could only  
be implemented by consuming the elements of the iterated sequence,  
with the `for` loop taking ownership of the elements individually.

While consuming iteration is sometimes what you want, _borrowing_ iteration is  
equally important, serves as a better default for noncopyable elements,  
and yet cannot be supported by the existing `Sequence`.

## Proposed solution

This proposal introduces a new `BorrowingSequence`, with iteration based around  
serving up `Span`s of elements, rather than individual elements, eliminating  
the need to copy. This pattern is also more optimizable in many cases where an  
iterated type is backed by contiguous memory.

## Detailed design

The `BorrowingSequence` protocol is defined as:

```swift
public protocol BorrowingSequence<Element>: ~Copyable, ~Escapable {
  /// A type representing the sequence's elements.
  associatedtype Element: ~Copyable

  /// A type that provides the sequence's iteration interface and
  /// encapsulates its iteration state.
  associatedtype BorrowingIterator: BorrowingIteratorProtocol<Element> & ~Copyable & ~Escapable

  /// Returns a borrowing iterator over the elements of this sequence.
  @lifetime(borrow self)
  func makeBorrowingIterator() -> BorrowingIterator
}

```

This protocol shape is very similar to the current `Sequence`. It has a primary  
associated type for the element, and just one method  
that hands you an iterator you can use to iterate the elements of the sequence.

The differences being:

- It allows conformance by noncopyable types. Note that copyable and escapable  
types can also conform---but the protocol is designed to not require it.
- The `Element` type is also not required to be copyable.
- There is a `BorrowingIterator` associated type, and a `makeBorrowingIterator()`  
method that returns one. These play a similar role to `Iterator` and `makeIterator()`  
on `Sequence`.
- The iterator returned by `makeBorrowingIterator` is constrained to the lifetime  
of the sequence. This allows the iterator to be implemented in terms of properties  
borrowed from the sequence (often a span of the sequence).

Note that the names of the associated type and method are specifically chosen to not conflict  
with the existing names on `Sequence`, allowing a type to have different implementations  
for both `BorrowingSequence` and `Sequence`.

The `BorrowingIteratorProtocol` is similar to its analog, but differs a little more:

```swift
public protocol BorrowingIteratorProtocol<Element>: ~Copyable, ~Escapable {
  associatedtype Element: ~Copyable
  
  /// Returns a span over the next group of contiguous elements, up to the
  /// specified maximum number.
  @lifetime(&self)
  mutating func nextSpan(maximumCount: Int) -> Span<Element>
  
  /// Advances this iterator by up to the specified number of elements and
  /// returns the number of elements that were actually skipped.
  mutating func skip(by maximumOffset: Int) -> Int
}

extension BorrowingIteratorProtocol where Element: ~Copyable {
  // default implementation
  public mutating func skip(by maximumOffset: Int) -> Int { ... }
}

```

Instead of offering up individual elements via `next()` as `IteratorProtocol` does,  
`BorrowingIteratorProtocol` offers up spans of elements. The iterator indicates there  
are no more elements to iterate by returning an empty `Span`.

How many elements are in each span is determined both by the conforming type  
and the caller. For the conforming type, the usual implementation will be to  
offer up the largest span possible for each call. In the case of `Array`, or other  
contiguously-stored types, this is just a single span for the entire collection.

Examples where `nextSpan` may be required to be called more than once include:

- _Types where elements are held in discontiguous storage,_ such as a ring buffer.  
A ring buffer would provide two spans: one from the first element to the end of the buffer,  
followed by one from the start of the buffer to the last element.
- _Types that produce elements on demand,_ such as a `Range`. Because the elements of a range  
aren't stored directly in memory, the iterator would provide access to a span  
of a single element at a time. Note that this is how any `Sequence` can be adapted to conform  
to `BorrowedSequence` (see later in this proposal).
- _Callers that only process a certain number of elements at a time_ would pass  
their limit as `maximumCount`, with successive calls to `nextSpan` until the returned  
span is empty.

Specifying a maximum number of elements is important for use cases where an  
iterator is passed `inout` to another function, which consumes only as many  
elements as it needs and no more. This is required as a result of the bulk iteration  
model, unlike `IteratorProtocol.next()` which returns only one element at a time.

Additionally, it provides a convenience for the caller.  
Because calling `nextSpan` is mutating, a caller that can only handle a specific number  
of elements at a time would otherwise need to write quite complex code to manage  
partial usage of a returned span.

The maximum count also gives signal to the iterator that only a specific number of  
elements are needed, which can be used to produce results more efficiently.  
For example, a lazily filtered span might want to serve up "runs" of filtered-in  
elements from the original collection, in which case you really want to know how many  
the caller actually wants to consume.

### Use of the new protocols

To illustrate how these new protocols would be used, we can also look at the proposed  
desugaring of the `for...in` syntax. The following familiar code:

```swift
for element in borrowingSequence {
    f(element)
    g(element)
}

```

would cause the compiler to generate code similar to:

```swift
var iterator = borrowingSequence.makeBorrowingIterator()
while true {
    let span = iterator.nextSpan(maximumCount: Int.max)
    if span.isEmpty { break }	  

    for i in span.indices {
        f(span[i])
        g(span[i])
    }
}

```

This automatic code generation is often referred to as "desugaring" the loop.

Note, the compiler will not necessarily generate this exact code,  
but it is illustrative of how a user might use the methods directly.  
The inner `for i in span.indices`, followed by access to the individual  
elements by subscripting with `i` wherever you would previously have  
use the loop variable, will be familiar to anyone who has  
iterated spans of noncopyable elements as they exist today. It allows  
noncopyable elements to be passed directly into functions like `f`  
and `g` without the need for a temporary variable.

This desugared `while` loop is a lot more complex than its `Sequence`  
equivalent. But the day-to-day usage remains exactly the same, with  
the added complexity left to the caller.

Note that in the case of `Array`, the new protocol results in much less overhead  
for the optimizer to eliminate. Iterating a `Span` in the inner loop is a lot  
closer to the "ideal" model of advancing a pointer over a buffer and accessing  
the elements directly. It is therefore expected that this design will result  
in better performance in some cases where today the optimizer is unable  
to eliminate the overhead of Swift's `Array`.

### Borrowing sequence operations

The addition of appropriate algorithms matching those on `Sequence` will be specified  
in an upcoming proposal. Much like `Sequence` operations, these will be written as  
extensions on the `BorrowingSequence` protocol. The following two examples demonstrate  
how some operations will be as simple as their `Sequence` counterparts, while others  
will require more careful manual iteration over spans.

The differences between `Sequence.reduce(into:_:)` and `BorrowingSequence.reduce(_:_:)`  
are only in the parameters' ownership annotations, because we only need a borrowing  
access to one element at a time. The implementation itself is essentially identical:

```swift
extension BorrowingSequence {
   func reduce<T: ~Copyable>(
      into initial: consuming T,
      _ nextPartialResult: (inout T, borrowing Element) -> Void
   ) -> T {
      var result = initial
      for element in self {
         nextPartialResult(&result, element)
      }
      return result
   }
}

```

For `elementsEqual`, however, we use manual iteration to compare spans of equal size between two `BorrowingSequences`:

```swift
extension BorrowingSequence where Self: ~Escapable & ~Copyable, Element: Equatable {
   func elementsEqual<S: BorrowingSequence<Element>>(
      _ rhs: borrowing S
   ) -> Bool
      where S: ~Escapable & ~Copyable
   {
      var iter1 = makeBorrowingIterator()
      var iter2 = rhs.makeBorrowingIterator()
      while true {
         var span1 = iter1.nextSpan(maximumCount: .max)
   
         if span1.isEmpty {
            // LHS is empty - sequences are equal iff RHS is also empty
            let span2 = iter2.nextSpan(maximumCount: 1)
            return span2.isEmpty
         }
   
         while span1.count > 0 {
            let span2 = iter2.nextSpan(maximumCount: span1.count)
            if span2.isEmpty { return false }
            for i in 0..<span2.count {
               if span1[i] != span2[i] { return false }
            }
            span1 = span1.extracting(droppingFirst: span2.count)
         }
      }
   }
}

```

### Standard Library adoption

`InlineArray` and the various `Span` types will conform to `BorrowingSequence`,  
with `Span` also conforming to `BorrowingIteratorProtocol` and acting as the iterator  
for all these types.

```swift
extension Span: BorrowingSequence, BorrowingIteratorProtocol
   where Self: ~Copyable & ~Escapable, Element: ~Copyable
{
   @lifetime(borrow self)
   func makeBorrowingIterator() -> Self

   @lifetime(&self)
   mutating func nextSpan(maximumCount: Int) -> Self
    
   mutating func skip(by maximumOffset: Int) -> Int
}

extension MutableSpan: BorrowingSequence
   where Self: ~Copyable & ~Escapable, Element: ~Copyable
{
   @lifetime(borrow self)
   func makeBorrowingIterator() -> Span<Element>
}

extension RawSpan: BorrowingSequence {
   @lifetime(borrow self)
   func makeBorrowingIterator() -> Span<UInt8>
}

extension MutableRawSpan: BorrowingSequence {
   @lifetime(borrow self)
   func makeBorrowingIterator() -> Span<UInt8>
}

extension InlineArray: BorrowingSequence
   where Self: ~Copyable & ~Escapable, Element: ~Copyable
{
   @lifetime(borrow self)
   func makeBorrowingIterator() -> Span<Element>
}

```

### Adaptors for existing `Sequence` types

As mentioned above, it is possible given an implementation to `Sequence`  
to implement the necessary conformance to `BorrowingSequence`. We propose  
the following addition to the standard library:

```swift
// An adaptor type that, given an IteratorProtocol instance, serves up spans
// of each element generated by `next` one 
public struct BorrowingIteratorAdapter<Iterator: IteratorProtocol>: BorrowingIteratorProtocol {
  var iterator: Iterator
  var currentValue: Iterator.Element? = nil

  public init(iterator: Iterator) {
    self.iterator = iterator
  }

  @lifetime(&self)
  public mutating func nextSpan(maximumCount: Int) -> Span<Iterator.Element> {
	// It may be surprising to some readers not used to Swift's ownership
	// model that currentValue is a stored property, not just a local variable.
	// This is because currentValue must be storage owned by the BorrowingIteratorAdapter
	// instance, in order to return a span of its contents with the specified lifetime.
    currentValue = iterator.next()
	// note Optional._span is a private method in the standard library
	// that creates an empty or 1-element span of the optional
    return curValue._span
  }
}

extension Sequence {
  public func makeBorrowingIterator() -> BorrowingIteratorAdapter<Iterator> {
    BorrowingIteratorAdapter(iterator: makeIterator())
  }
}

```

Given this, it will be possible for all types conforming to `Sequence` to also  
conform to `BorrowingSequence`. The conformance of existing sequence types,  
like `Array`, `Dictionary`, and `UnfoldSequence`, will be included in an  
upcoming proposal. Some of these types (such as `Array`) will merit a custom  
conformance exposing an underlying `Span`. For other types, the conformance  
will trivially make use of the `BorrowingIteratorAdaptor` shown above:

```swift
// all requirements fulfilled by BorrowingIteratorAdaptor
extension UnfoldSequence: BorrowingSequence { }

```

### Supressed conformance of the `BorrowingIterator` associated type

Proposal [SE-0503](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0503-suppressed-associated-types.md)  
allows the supression of `Copyable` and `Escapable` on associated types.

Since `Element` is a _primary_ associated type, users will be able to extend  
`BorrowingSequence` with algorithms that require copyability. For example,  
an algorithm to return the minimum element requires the ability to  
copy that minimum element in order to return it. Extensions on `BorrowingSequence`  
without specifying otherwise will default to `Element` being copyable, as  
specified by SE-0503. Swift users unaware of the concept of noncopyable  
types can extend `BorrowingSequence` without being aware of that feature of  
the language.

The `BorrowingIterator` associated type is _not_ a primary associated type; rather  
it's an implementation detail of a `BorrowingSequence`. Therefore  
per SE-0503, it will _not_ default to being either copyable or escapable.

This is a highly desirable property. While `Sequence` iterators are copyable,  
the actual behavior of a particular iterator when you iterate, copy it,  
then iterate the copy, is very implementation dependent.  
The `Sequence` protocol gives no specifications for how an iterator should  
behave when you do this in a generic context. As such,  
writing generic code that relies on being able to copy an iterator is  
never appropriate.

Additionally, many types (like `Span` or `InlineArray`) will require  
a nonescaping iterator. The preferred behavior for users, even  
those not aware that noncopyable or nonescapable types exist, is  
to allow extensions written against `BorrowingSequence` to "just work",  
on those types, since needing to copy or escape an iterator is rarely bumped  
into when implementing most sequence algorithms.

### `for` loop desugaring when both protocols are available

Note that in this case, it is likely that using `BorrowingSequence` to iterate  
`UnfoldSequence` will be slightly _less_ efficient than using its `Sequence`  
conformance, depending on the element type, given the overhead of moving the  
element into the `currentValue` storage. If the element is non-trivial, this  
may result in additional copies being made with the new borrowing iteration  
model, where with the existing `next()` operation, the element can be returned  
and consumed directly into its destination.

For example, imagine using `UnfoldSequence` to generate strings that are being streamed  
into an `Array<String>`:

- with `Sequence`, the strings are produced by the `UnfoldSequence`, returned by `next()`,  
and consumed into the `Array` with no logical copies
- with `BorrowingSequence`, the strings are produced, stored in the `currentValue` variable,  
**copied** out of the `Span` of that variable into the `Array`, and then the value in  
`currentValue` is destroyed.

For this reason, it may not be appropriate to switch all `for` iteration to use  
`BorrowingSequence` when `Sequence` is available. How to determine which cases are  
better (such as `Array` is expected to be) and which are worse (such as the `UnfoldSequence`  
example above) needs further investigation.

An experimental feature, `BorrowingForLoopByDefault`, will be made available  
to allow switching of the compiler to default to always use the new desugaring,  
to allow for experimentation with the impact on performance.

Regardless, types that _do not_ offer a `Sequence` conformance today (noncopyable types,  
`InlineArray`, and `Span` itself) will use the new desugaring. Making that switch is left  
to future proposals. For now, if a `Sequence` conformance is available, it will be used  
even if `BorrowingSequence` is also available.

## Source compatibility

This proposal introduces a new protocol, `BorrowingSequence`.  
Existing conformers to `Sequence` can also implement `BorrowingSequence`. This is  
entirely source compatible.

As discussed above, switching to use the new `BorrowingSequence` protocol  
by default could impact runtime performance, and so while source compatible,  
needs further control.

There is an additional source compatibility edge case that would be caused  
by changing `for` loops to :

```swift
var array: [Int] = // ...
for element in array {
  // when used with BorrowingSequence, this
  // would cause a compile time error
  array[0] = 1
}

```

This is similar to a simpler example where the reason is clearer:

```swift
var array: [Int] = // ...
let span = array.span
// error: Overlapping accesses to 'array', but modification requires exclusive access
array[0] = 99

```

Now, it is usually unwise or unintentional to modify a collection while iterating over it.  
Often the user does not realize that this triggers copy-on-write for the collection  
they are iterating. But this potential source of breakage that should be considered.

## ABI compatibility

This proposal adds two new protocols, the `BorrowingIteratorAdapter` type, and  
conformances for the span and `InlineArray` types to the standard library ABI.  
An [upcoming proposal](https://forums.swift.org/t/pitch-reparenting-resilient-protocols/84189) will provide a language feature allowing re-parenting  
of the `Sequence` protocol with `BorrowingSequence`, and a follow-up proposal to this  
one will include that re-parenting and additional standard library API.

## Future directions

### Modifying `Sequence` to extend `BorrowingSequence`

A future proposal will provide the details of allowing re-parenting of  
a protocol in an ABI compatible way, along with a modification of the existing  
`Sequence` proposal to make it extend the new `BorrowingSequence` protocol proposed here.  
The insertion of `BorrowingSequence` into the existing `Sequence` protocol hierarchy  
will allow for algorithms that target both copyable and noncopyable sequences.

### Other forms of iteration

While this proposal focuses on borrowing iteration, there are multiple other kinds of  
iteration that are equally important. Future proposals may take up alternative designs  
for the following kinds of iteration:

- _Consuming iteration:_ A container may want to provide consuming iteration of its  
elements. For example, a container of noncopyable elements would need to be consumed  
when appended to a different container.
- _"Draining" iteration:_ Similar to consuming iteration, but preserving the existence  
and allocation of the original container. This can be thought of as consuming the  
elements, but not the container itself.
- _Mutating iteration:_ A mutable collection or a container of noncopyable elements  
may want to provide in-place mutation of its elements during iteration.
- _Generative iteration:_ Certain types generate their elements during iteration, rather  
than storing them in memory, like iterating an `UnfoldSequence` or the key-value pairs  
in a dictionary. A different iteration model could be more performant than the  
borrowing iteration proposed here.

## Alternatives considered

### Adding noncopyable support to `Sequence`

Another approach for providing iteration and sequential algorithms  
to noncopyable types added defaulted requirements to the existing `Sequence`  
protocol.

## Acknowledgments

Many thanks to Karoy Lorentey, Kavon Favardin, Joe Groff, Tony Parker, and Alejandro Alonso, for their input into this proposal.

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [January 24, 2026, 3:27am UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/2 "2026-01-24T03:27:20Z")

</div>

This is a good “filling out the table” PR, and the proposal is well put together. Still, Sequence is Not Great for a variety of reasons, though some of them don’t apply to BorrowingSequence. I guess I’d ask: what types do we expect to conform to BorrowingSequence that aren’t Collections? As I see it, a BorrowingIterator that generates its elements dynamically isn’t well-represented by a BorrowingSequence, since nothing stops you from calling `makeBorrowingIterator` multiple times.

(Sequence is Not Great? [At least in my opinion.](https://belkadan.com/blog/2021/08/Swift-Regret-Sequence/?tag=swift-regrets))

---

<div class="post-metadata">

**Author:** ![ben-cohen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ben-cohen/32/296_2.png) [@ben-cohen](https://forums.swift.org/u/ben-cohen)\
**Post date:** [January 24, 2026, 3:46am UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/3 "2026-01-24T03:46:46Z")

</div>

> [@jrose](#):
>
> what types do we expect to conform to BorrowingSequence that aren’t Collections?

This deserves a longer response but here are two quick examples:

- C++ types that present an `*iterator++` interface. These cannot be adapted to collections because even if you consider `iterator` the "index", they only support `==` not `<`. Even if you say "ok, let's loosen that requirement on `Index`", you are still left with the issue of detecting that an index is "invalidated", and so should trap when you apply it to a collection (instead of just returning the dereferenced value the iterator provides when you fake-index into the collection that)
- Sequences that are more efficiently implemented as "update a value in-place and lend that you each time". An example of this would be every permutations of a sequence, which can be done by efficiently mutating a value to represent each permutation. Yes, you can simulate this by just stuffing the state into an index and permuting that index via `index(after:)` but this is, tbf, a brittle hack.

More generally, I do believe that "present an efficient way to `for...in` over this things" is a fundamental building block. There's a reason why every language offers such a concept, and _relatively few_ languages provide a "stably index into this position in a collection".

---

<div class="post-metadata">

**Author:** ![jrose](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/jrose/32/408_2.png) [@jrose](https://forums.swift.org/u/jrose)\
**Post date:** [January 24, 2026, 3:52am UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/4 "2026-01-24T03:52:23Z")

</div>

Those are both good borrowing _iterators,_ they are not good borrowing _sequences_—because you can't iterate them multiple times. It may ultimately make sense to continue perpetuating the mistake of Sequence just for consistency, but I don't personally think these examples are especially motivating.

---

<div class="post-metadata">

**Author:** ![ben-cohen](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/ben-cohen/32/296_2.png) [@ben-cohen](https://forums.swift.org/u/ben-cohen)\
**Post date:** [January 24, 2026, 5:19am UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/5 "2026-01-24T05:19:49Z")

</div>

The permutation example is not so compelling, maybe – though the more generalized "unfold sequence, where you update the yielded element in-place" might be. But "bring idiomatic C++ iterables into Swift idiomatically" is certainly a real need.

I'm sympathetic to "the Iterable protocol is the fundamental one, and it's single pass" but that hits the problem that you still need to _get_ the iterator. When you go down this path, you end up creating a protocol for starting iteration and just end up with `Sequence`.

My view is that `Sequence`-like is still the right design, and that the community should adhere to the convention that sequences should try to always be multi-pass, with the "get iterator" operator resuming from the start every time.

I think "convention" is the right way to characterize this rather than "protocol requirement" because it's really not enforceable. When you have sequences that use closures (like lazy maps or unfolding sequences) you can't avoid making it possible to make non-multi-pass sequences. But the convention should be this is bad.

As for things you can't do in multi-pass ways, like generate random numbers or consume a file: this just shouldn't be sequences. They should be another thing. Either concrete methods like `readLine()` or maybe we need another protocol (or _maybe_ that protocol is `Iterator`). But those should not be used with `for...in`, but rather a loop like `while let line = readLine() { }`. But this protocol is about things you _can_ `for..in` over.

---

<div class="post-metadata">

**Author:** ![dnadoba](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dnadoba/32/11438_2.png) [@dnadoba](https://forums.swift.org/u/dnadoba)\
**Post date:** [January 24, 2026, 5:42am UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/6 "2026-01-24T05:42:30Z")

</div>

This is an exciting feature, especially once `Sequence` inherits from it. SwiftUI is currently using `Sequence.withContiguousStorageIfAvailable(_:)` for a couple fast paths and will gladly move to this.

The `BorrowingIteratorAdapter` that only hands over a `Span` with a single Element isn’t ideal for `Sequence`s that are passed across ABI boundaries and can’t be fully specialized/inlined. It would likely be more efficient if each call to `nextSpan` would return a couple elements. Objective-C’s fast enumeration allocates storage on the stack for a couple elements (IIRC 16) which is then filled with elements by a call to [`countByEnumerating`](https://developer.apple.com/documentation/foundation/nsfastenumeration/countbyenumerating(with:objects:count:)). `BorrowingIteratorAdapter` could in theory do the same if Swift had a version similar to `InlineArray` that would be allowed to be partially initialized and then using that instead of `var currentValue: Iterator.Element?` and calling `next()` multiple times before returning a `Span` with multiple elements.

(Note that this largely assumes that `BorrowingIteratorAdapter.nextSpan(maximumCount:)` is specialized for the concrete Sequence which will in practice require specialized protocol witness tables so that another module (e.g. SwiftUI) can call into this specialized witness through the protocol witness table)

---

<div class="post-metadata">

**Author:** ![benrimmington](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/benrimmington/32/695_2.png) [@benrimmington](https://forums.swift.org/u/benrimmington)\
**Post date:** [January 24, 2026, 5:45am UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/7 "2026-01-24T05:45:45Z")

</div>

How will _bidirectional_ iteration work for noncopyable types?

(Custom iterators can be more efficient than the default `IndexingIterator`, but they only work in the forwards direction. `ReversedCollection` uses the base collection's indices.)

---

<div class="post-metadata">

**Author:** ![ellie20](https://avatars.discourse-cdn.com/v4/letter/e/6f9a4e/32.png) [@ellie20](https://forums.swift.org/u/ellie20)\
**Post date:** [January 24, 2026, 7:50am UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/8 "2026-01-24T07:50:05Z")

</div>

The `nextSpan` method, as written, returns a `Span` with a scoped lifetime dependency on the iterator, which lets it support "update a value in-place" iterators.

But it's actually stricter than necessary if the sequence just stores its elements in memory. I think the most permissive lifetime in that case would be for the returned `Span` to have a _copied_ lifetime dependency on the iterator. That would effectively mean the returned `Span` had a scoped lifetime dependency on the sequence (because the iterator itself has a scoped lifetime dependency on the sequence).

I wonder to what extent supporting that flexibility would be important in practice. For comparison, iterators in Rust work like that, where (the equivalent of) borrowing iterators yield elements with a scoped lifetime dependency on the original sequence, not the iterator itself; and "update a value in-place" iterators (sometimes called "lending iterators") are not supported.

* * *

Could borrowing sequences be extended to support non-escapable elements in the future?

* * *

I wonder how the new iteration protocols could be extended to support generators in the future. One thing is that the local variables of a generator need to be "non-movable", in case they are borrowed or mutated across suspend points. So an iterator based on a generator would either have to be "non-movable" or store the local variables on the heap.

* * *

I don't know if it's a good idea for `Span` to be a borrowing iterator. I'm mostly concerned about the borrowing iterator methods being available on `Span`, which could be unintuitive if people don't normally think of `Span` as being an iterator.

It could also be error-prone because `Span` is implicitly copyable, so it could be non-obvious that passing a `Span` into a for loop advances a copy, not the original. In Rust, the `Range` type, which is its own iterator type, was chosen to not be implicitly copyable, because [implicitly-copyable iterators were discovered to be error-prone](https://github.com/rust-lang/rust/issues/18045).

---

<div class="post-metadata">

**Author:** ![fclout](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/fclout/32/36155_2.png) [@fclout](https://forums.swift.org/u/fclout)\
**Post date:** [January 24, 2026, 8:54am UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/9 "2026-01-24T08:54:25Z")

</div>

I’m looking forward to first-class support for iterating over collections of non-copyable things. I have two main comments coming out of my read:

First, I think will often be implemented wrong. The convenience of returning more elements than requested is going to be an attractive nuisance, and it’s going to _usually_ work because the common use cases won’t specify a maximum size. It’s common to limit the number of elements when the caller passes in a buffer to copy into, but it’s unusual and unintuitive that you would have to limit the number of elements when you own the storage.

The proposal says it saves quite a bit of complexity on the caller side that you can put a limit on the number of elements, but that logic is/should be the same for all iterators. Aside from implementations that outright ignore the maximum size, I’m also somewhat concerned that some details will be inconsistent across implementations, like what happens if you request a size less than 1. Could we be better off with an iterator protocol that doesn’t let you specify maximum counts, and provide a standard generic wrapper that lets you request smaller chunks at a time? This would make the implementation of iterators dead easy without adding real complexity to algorithms that need to work with chunks.

Second, Element must be Escapable. I imagine this is in part because Span elements must be Escapable. I wanted to check what the evolution options are on that front. If we ever want iterators that support non-Escapable types, will we need a new protocol hierarchy again or would we in theory be able to retrofit a suppressed Escapable conformance on those?

---

<div class="post-metadata">

**Author:** ![Lancelotbronner](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lancelotbronner/32/22071_2.png) [@Lancelotbronner](https://forums.swift.org/u/Lancelotbronner)\
**Post date:** [January 24, 2026, 4:34pm UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/10 "2026-01-24T16:34:12Z")

</div>

I personally would prefer a separate SpanIterator rather than implementing BorrowingIteratorProtocol directly as that “pollutes” the API of Span with “weird” functions.

---

<div class="post-metadata">

**Author:** ![Nevin](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nevin/32/617_2.png) [@Nevin](https://forums.swift.org/u/Nevin)\
**Post date:** [January 24, 2026, 4:36pm UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/11 "2026-01-24T16:36:02Z")

</div>

This is a really minor point, but I think `iterator.skip(by: n)` does not read particularly well.

It should either be `iterator.skip(n)` or something like `iterator.advance(by: n)`.

---

<div class="post-metadata">

**Author:** ![Dmitriy\_Ignatyev](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/dmitriy_ignatyev/32/11340_2.png) [@Dmitriy\_Ignatyev](https://forums.swift.org/u/Dmitriy_Ignatyev)\
**Post date:** [January 24, 2026, 7:46pm UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/12 "2026-01-24T19:46:33Z")

</div>

> [@ben-cohen](#):
>
> Another std lib noncopyable adoption pitch for you.

A very welcome surprise, this has been high on my wish list for a while.

> [@ben-cohen](#):
>
> Because the elements of a range  
> aren't stored directly in memory, the iterator would provide access to a span  
> of a single element at a time

As mentioned in the proposal, borrowing iteration can sometimes be less performant compared to the current copyable variant. There are already several ways to iterate over a sequence, and depending on the context, especially in generics, certain methods might outperform others. For example, iterating over indices and accessing elements by index, using a `for`loop, `forEach`, or `withContiguousStorageIfAvailable` could be faster depending on the constraints involved.

That said, adding borrowing iteration as a feature is a great step forward, and it addresses a real need. The challenge arises in scenarios where:

1. Borrowing iteration ends up being noticeably less performant.
2. Borrowing iteration _feels_ like it should be more performant than it actually is.

This could lead some users to mistakenly choose it, while others might end up needing to check performance across yet another iteration method.

In the case of Ranges, particularly for the most common scenario of Ranges of integers, what's the reasoning behind choosing borrowing iteration? With `Int` values, for example, it's often simpler and faster to just copy the value rather than borrowing it.

While I understand that we **can** add `BorrowingSequence` conformance to all sequences, the question is: **should** we? Especially for Copyable types where borrowing has no benefits or even worsen performance.

From a performance perspective (and perhaps in terms of generated code size), it seems like the compiler should be able to choose the best iteration model by default for `Copyable` values (e.g., the `Int` family), and opt for borrowing iteration when it's more beneficial (e.g., with large structs to avoid copy overhead). Users could still choose a different method explicitly when needed.

For non-copyable types, though, borrowing iteration by default makes perfect sense.

For copyable types, it might be useful to allow a type implementing the `Collection` protocol to choose the default iteration model based on certain conditions. To be clear, I'm not suggesting this as part of the current proposal, but rather as a potential direction for future exploration.

---

<div class="post-metadata">

**Author:** ![ellie20](https://avatars.discourse-cdn.com/v4/letter/e/6f9a4e/32.png) [@ellie20](https://forums.swift.org/u/ellie20)\
**Post date:** [January 24, 2026, 11:08pm UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/13 "2026-01-24T23:08:48Z")

</div>

> [@ben-cohen](#):
>
> There is an additional source compatibility edge case that would be caused  
> by changing `for` loops to :
> 
> ```swift
> var array: [Int] = // ...
> for element in array {
> // when used with BorrowingSequence, this
> // would cause a compile time error
> array[0] = 1
> }
> 
> ```

Could the for loop not create an implicit copy of `array`, and then perform a borrowing iteration on that, to preserve source compatibility? Then the implicit copy could be optimized away when it's not needed. In situations where the implicit copy can't be optimized away (such as if `array` is a stored property of a class instance, where changing a copy to a borrow could cause dynamic exclusivity checks to fail), a more general `borrow` operator could be used, which would generalize to any operation that would otherwise perform an implicit copy (such as passing to a `borrowing` function argument).

---

<div class="post-metadata">

**Author:** ![fclout](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/fclout/32/36155_2.png) [@fclout](https://forums.swift.org/u/fclout)\
**Post date:** [January 24, 2026, 11:36pm UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/14 "2026-01-24T23:36:59Z")

</div>

If we end up keeping the maximum size APIs (or even if we don’t, I guess), it could help to have another protocol like `BorrowingContiguousCollection` that extends `BorrowingSequence` with only a `span` property and implements `BorrowingSequence` for you. This could be its own separate proposal, though.

---

<div class="post-metadata">

**Author:** ![filip-sakel](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/filip-sakel/32/8697_2.png) [@filip-sakel](https://forums.swift.org/u/filip-sakel)\
**Post date:** [January 25, 2026, 3:22am UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/15 "2026-01-25T03:22:28Z")

</div>

> [@ben-cohen](#):
>
> ```swift
> associatedtype Element: ~Copyable
> 
> ```

Perhaps I missed something, but why isn’t Element also marked `~Escapable`? I think something like an InlineArray holding non-escapable closures might be a useful sequence type.

---

<div class="post-metadata">

**Author:** ![saagarjha](https://avatars.discourse-cdn.com/v4/letter/s/85f322/32.png) [@saagarjha](https://forums.swift.org/u/saagarjha)\
**Post date:** [January 25, 2026, 5:08am UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/16 "2026-01-25T05:08:39Z")

</div>

I haven't read this super carefully yet, but is there a plan to adopt something similar with `AsyncSequence` as well? Since it would have even more benefit there by skipping awaits in many cases.

---

<div class="post-metadata">

**Author:** ![OneSadCookie](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/onesadcookie/32/3205_2.png) [@OneSadCookie](https://forums.swift.org/u/OneSadCookie)\
**Post date:** [January 25, 2026, 8:29am UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/17 "2026-01-25T08:29:06Z")

</div>

> [@ellie20](#):
>
> Could the for loop not create an implicit copy of `array`, and then perform a borrowing iteration on that, to preserve source compatibility?

I think that would create an even-more-subtle source incompatibility, where modifications to elements later in the iteration wouldn't be seen by the iteration

---

<div class="post-metadata">

**Author:** ![Slava\_Pestov](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/slava_pestov/32/30204_2.png) [@Slava\_Pestov](https://forums.swift.org/u/Slava_Pestov)\
**Post date:** [January 25, 2026, 5:21pm UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/18 "2026-01-25T17:21:50Z")

</div>

> [@OneSadCookie](#):
>
> I think that would create an even-more-subtle source incompatibility, where modifications to elements later in the iteration wouldn't be seen by the iteration

That’s how `for … in …` loops work today.

---

<div class="post-metadata">

**Author:** ![Val](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/val/32/23590_2.png) [@Val](https://forums.swift.org/u/Val)\
**Post date:** [February 11, 2026, 1:22pm UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/19 "2026-02-11T13:22:32Z")

</div>

Is `Element: Escapable` a fundamental constraint? I just looked at my personal use cases for a BorrowingSequence today and all of them require non escapable elements.

---

<div class="post-metadata">

**Author:** ![glessard](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/glessard/32/409_2.png) [@glessard](https://forums.swift.org/u/glessard)\
**Post date:** [February 27, 2026, 5:08pm UTC](https://forums.swift.org/t/pitch-borrowing-sequence/84332/20 "2026-02-27T17:08:10Z")

</div>

It is not. We don’t yet have support for escapable elements from pointers, which is a first prerequisite for this. We also need a way to spell multiple escapability requirements in the same type, for example one for the container and another for the elements (or even per stored property.)

[Next page](https://forums.swift.org/t/pitch-borrowing-sequence/84332.md?page=2)
