# \[Pitch #2\] Safe Access to Contiguous Storage

**URL:** <https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745>\
**Category:** Pitches\
**Created:** [June 25, 2024, 8:43pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745 "2024-06-25T20:43:05Z")\
**Posts on this page:** 20\
**Page:** 1

<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:** [June 25, 2024, 8:43pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/1 "2024-06-25T20:43:05Z")

</div>

We are proposing a new abstraction for safe, direct access ot memory: `Span`. This is a substantial update to our earlier pitch, ["Safe Access to Contiguous Storage"](https://forums.swift.org/t/69888), and is a companion to the [Non-Escapable Types](https://github.com/apple/swift-evolution/pull/2304) proposal.

Some of the changes in this version are:

- The type's name is changed to `Span`. As a name, `Span` is short, pithy, precedented, and [composable](https://forums.swift.org/t/72742).

- We add an untyped counterpart to `Span`: `RawSpan`, along with a motivating parser example and parsing utilities for `RawSpan`.

- The Indexing model is simplified to use integer offsets.

- Most of the `Collection`-like API is removed, and we use the slicing alternative adopted for `UnsafeBufferPointer` in [SE-0437 Noncopyable Standard Library Primitives](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0437-noncopyable-stdlib-primitives.md). This leaves more room to get forthcoming `Sequence`-like and `Collection`-like improvements right.

We have added a prototype of `Span` to a [branch](https://github.com/apple/swift-collections/tree/future) of the [swift-collections](https://github.com/apple/swift-collections) package. This branch requires a recent Swift [development toolchain](https://www.swift.org/download/#snapshots).

Full proposal: [Safe Access to Contiguous Storage](https://github.com/swiftlang/swift-evolution/blob/e924bab3d24424271982613e5dec24c699bc9dd0/proposals/nnnn-safe-shared-contiguous-storage.md) ([#2307](https://github.com/swiftlang/swift-evolution/pull/2307))

# Safe Access to Contiguous Storage

- Proposal: SE-NNNN
- Authors: [Guillaume Lessard](https://github.com/glessard), [Andrew Trick](https://github.com/atrick), [Michael Ilseman](https://github.com/milseman)
- Review Manager: TBD
- Roadmap: [BufferView Roadmap](https://forums.swift.org/t/66211)
- Bug: rdar://48132971, rdar://96837923
- Implementation: Prototyped in [GitHub - apple/swift-collections: Commonly used data structures for Swift](https://github.com/apple/swift-collections) (on branch "future")
- Review: ([pitch 1](https://forums.swift.org/t/69888))

## Introduction

We introduce `Span<T>`, an abstraction for container-agnostic access to contiguous memory. It will expand the expressivity of performant Swift code without comprimising on the memory safety properties we rely on: temporal safety, spatial safety, definite initialization and type safety.

In the C family of programming languages, memory can be shared with any function by using a pointer and (ideally) a length. This allows contiguous memory to be shared with a function that doesn't know the layout of a container being used by the caller. A heap-allocated array, contiguously-stored named fields or even a single stack-allocated instance can all be accessed through a C pointer. We aim to create a similar idiom in Swift, without compromising Swift's memory safety.

This proposal is related to two other features being proposed along with it: [Nonescapable types](https://github.com/apple/swift-evolution/pull/2304) (`~Escapable`) and [Compile-time Lifetime Dependency Annotations](https://github.com/swiftlang/swift-evolution/pull/2305), as well as related to the [BufferView roadmap](https://forums.swift.org/t/66211) forum thread. This proposal is also related to the following proposals:

- [SE-0426](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0426-bitwise-copyable.md) BitwiseCopyable
- [SE-0427](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0427-noncopyable-generics.md) Noncopyable generics
- [SE-0377](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0377-parameter-ownership-modifiers.md) `borrowing` and `consuming` parameter ownership modifiers
- [SE-0256](https://github.com/swiftlang/swift-evolution/blob/main/proposals/0256-contiguous-collection.md) `{Mutable}ContiguousCollection` protocol (rejected, superseded by this proposal)

## Motivation

Swift needs safe and performant types for local processing over values in contiguous memory. Consider for example a program using multiple libraries, including one for [base64](https://datatracker.ietf.org/doc/html/rfc4648) decoding. The program would obtain encoded data from one or more of its dependencies, which could supply the data in the form of `[UInt8]`, `Foundation.Data` or even `String`, among others. None of these types is necessarily more correct than another, but the base64 decoding library must pick an input format. It could declare its input parameter type to be `some Sequence<UInt8>`, but such a generic function significantly limits performance. This may force the library author to either declare its entry point as inlinable, or to implement an internal fast path using `withContiguousStorageIfAvailable()`, forcing them to use an unsafe type. The ideal interface would have a combination of the properties of both `some Sequence<UInt8>` and `UnsafeBufferPointer<UInt8>`.

The `UnsafeBufferPointer` passed to a `withUnsafeXXX` closure-style API, while performant, is unsafe in multiple ways:

1. The pointer itself is unsafe and unmanaged
2. `subscript` is only bounds-checked in debug builds of client code
3. It might escape the duration of the closure

Even if the body of the `withUnsafeXXX` call does not escape the pointer, other functions called inside the closure have to be written in terms of unsafe pointers. This requires programmer vigilance across a project and potentially spreads the use of unsafe types, even when it could have been written in terms of safe constructs.

We want to take advantage of the features of non-escapable types to replace some closure-taking API with simple properties, resulting in more composable code:

```swift
let array = Array("Hello\0".utf8)

// Old
array.withUnsafeBufferPointer {
  // use `$0` here for direct memory access
}

// New
let span: Span<UInt8> = array.storage
// use `span` in the same scope as `array` for direct memory access

```

## Proposed solution

`Span` will allow sharing the contiguous internal representation of a type, by providing access to a borrowed view of an interval of contiguous memory. A view does not copy the underlying data: it instead relies on a guarantee that the original container cannot be modified or destroyed during the lifetime of the view. `Span`'s lifetime is statically enforced as a lifetime dependency to a binding of the type vending it, preventing its escape from the scope where it is valid for use. This guarantee preserves temporal safety. `Span` also performs bounds-checking on every access to preserve spatial safety. Additionally `Span` always represents initialized memory, preserving the definite initialization guarantee.

By relying on borrowing, `Span` can provide simultaneous access to a non-copyable container, and can help avoid unwanted copies of copyable containers. Note that `Span` is not a replacement for a copyable container with owned storage; see the future directions for more details ([Resizable, contiguously-stored, untyped collection in the standard library](#Bytes))

`Span` is the currency type for local processing over values in contiguous memory. It is the replacement for any API currently using `Array`, `UnsafeBufferPointer`, `Foundation.Data`, etc., that does not need to escape the value.

### `RawSpan`

`RawSpan` allows sharing the contiguous internal representation for values which may be heterogenously-typed, such as in decoders. Since it is a fully concrete type, it can achieve better performance in debug builds of client code as well as a more straight-forwards understanding of performance for library code.

`Span<some BitwiseCopyable>` can always be converted to `RawSpan`, using a conditionally-available property or a constructor.

Please see the full proposal for the [Detailed Design](https://github.com/swiftlang/swift-evolution/blob/1844c97f09ec017308bc181b4af28630075ce3c3/proposals/nnnn-safe-shared-contiguous-storage.md#detailed-design), [Alternatives Considered](https://github.com/swiftlang/swift-evolution/blob/1844c97f09ec017308bc181b4af28630075ce3c3/proposals/nnnn-safe-shared-contiguous-storage.md#alternatives-considered) and [Future Directions](https://github.com/swiftlang/swift-evolution/blob/1844c97f09ec017308bc181b4af28630075ce3c3/proposals/nnnn-safe-shared-contiguous-storage.md#future-directions) sections.

---

<div class="post-metadata">

**Author:** ![Karl](https://avatars.discourse-cdn.com/v4/letter/k/34f0e0/32.png) [@Karl](https://forums.swift.org/u/Karl)\
**Post date:** [June 26, 2024, 3:21am UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/2 "2024-06-26T03:21:42Z")

</div>

> [@glessard](#):
>
> It could declare its input parameter type to be `some Sequence<UInt8>`, but such a generic function significantly limits performance. This may force the library author to either declare its entry point as inlinable, or to implement an internal fast path using `withContiguousStorageIfAvailable()`, forcing them to use an unsafe type. The ideal interface would have a combination of the properties of both `some Sequence<UInt8>` and `UnsafeBufferPointer<UInt8>`.

I think this could be phrased better.

Of course, please correct me if I'm misunderstanding, but I _believe_ what you're trying to say is that an _unspecialised_ generic function is not as efficient as a specialised one, or one which operates on a concrete type (because unspecialised functions still have witness table indirection that hasn't been optimised out). If that is what was meant, it's worth noting that the internal fast path does not _need_ to use an unsafe type. For instance, this would be a legitimate (albeit somewhat limited) implementation of a fast path:

```swift
public func parse(_ input: some Sequence<UInt8>) {
  // Downcast to common concrete types
  // to encourage specialisations for them.
  if let arrayInput = input as? Array<UInt8> {
    return _parseImpl(arrayInput)
  } else if let dataInput = input as? Data {
    return _parseImpl(dataInput)
  }
  return _parseImpl(input)
}

internal func _parseImpl(_ input: some Sequence<UInt8>) {
  // ...
}

```

My understanding is that this is approximately how the `@_specialize` attribute works.

But when I read the paragraph I quoted, it comes across like you're saying generics are just inherently slow, or that we're actually looking for the performance of unsafe buffer pointers. This same paragraph was in the previous draft and when I read it months ago, it came across the same way.

That said, I think the overall point you're making is sound: that there exists a very large class of collections which have contiguous storage, and when generic algorithms are compiled, we want them to be treated as equivalent at a very low level (controlling specialisation behaviour is a very low level thing that doesn't often bubble up in to the high level language semantics IMO). [I've been doing this](https://github.com/karwa/swift-url/blob/01ad5a103d14839a68c55ee556513e5939008e9e/Sources/WebURL/IPAddress.swift#L548-L566) in my libraries for years (using `wCSIA` and a bounds-checked buffer pointer rather than `as?` casts, but conceptually serving the same purpose), but for me the motivation hasn't been performance - all the code is still generic, and it's `@inlinable` so it _could_ be specialised - instead, the motivation is code size.

For a parser distributed as a Swift package, I don't want the final application binary to have separate copies of the parsing logic if the input data happens to be a `Array<UInt8>`, `Data`, `UnsafeBufferPointer<UInt8>` (e.g. a C string), etc - I want those to collapse to a single specialisation because the final binary will have to lug those around.

* * *

More broadly, while I support the overall direction, I am very concerned about the lack of a `Collection` interface. I've shown how I use this pattern currently (with generic code), and to me it is very important that this remains something of a low-level implementation detail that does not leak in to interfaces unless contiguous storage specifically is required, and that I can continue to write generic code which works with both `Span`-providing collections and non-contiguous (or non-updated) collections.

I understand that we still need to work out whether or how `Collection` can be adapted to non-copyable types. I believe that work should be a prerequisite for actually adding this type to the standard library.

---

<div class="post-metadata">

**Author:** ![Karl](https://avatars.discourse-cdn.com/v4/letter/k/34f0e0/32.png) [@Karl](https://forums.swift.org/u/Karl)\
**Post date:** [June 26, 2024, 3:34am UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/3 "2024-06-26T03:34:58Z")

</div>

Also, I believe all of the typed operations on `RawSpan` are unsafe. For instance:

- `func view<T: BitwiseCopyable>(as: T.Type) -> Span<T>` is not safe.

- `func load<T>(fromByteOffset offset: Int = 0, as: T.Type) -> T`

- `func loadUnaligned<T: BitwiseCopyable>(fromByteOffset offset: Int = 0, as: T.Type) -> T`

A programmer error could use these functions to construct an enum with an invalid bit-pattern (doesn't match any defined case), and I don't believe the language makes any guarantees that such values would be handled consistently. Your code might go down all kinds of bizarre and non-sensical execution paths and it could even change between compiler versions (in other words, UB). The load _operation_ itself would be fine; it's the _result_ which could be problematic.

It's good to offer them, but I think they should have `unsafe` in the name. We are relying on the programmer to manually reason about the type-safety of what they're doing.

---

<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:** [June 26, 2024, 5:29am UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/4 "2024-06-26T05:29:32Z")

</div>

`view(as:)` and `loadUnaligned(as:)` are both limited to `BitwiseCopyable` types and, as such, it is possible for them to produce invalid values (that is, values that an initializer would never produce). I believe that possibility is marked by the particle "raw" in the type name, just like `UnsafeRawPointer`. There are different types of unsafety and I believe it is useful to use different words for each when possible. (I noted that there is a stern warning only for the versions that don't check bounds – this needs a better balance.) In the case of `load<T>(as:)`, there is no constraint on the type, and that's significantly more unsafe. We could add a `BitwiseCopyable` constraint on this as well, thereby limiting the fully-unconstrained `load` operation to being used via `UnsafePointer` in a `withUnsafeBytes()` closure.

---

<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:** [June 26, 2024, 5:43am UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/5 "2024-06-26T05:43:23Z")

</div>

> [@Karl](#):
>
> I understand that we still need to work out whether or how `Collection` can be adapted to non-copyable types. I believe that work should be a prerequisite for actually adding this type to the standard library.

These are the first non-escapable types to be pitched, but we do not yet know in which order things will land in the standard library. But we need some concrete things to build upon.

`Span`, `~Escapable` types and `~Copyable` types need to be prototyped and used so that the generalized `Sequence`-like and `Collection`-like don't get designed in a vacuum. Pitching and prototyping the feature also allows the adventurous like you to try things out and point out mistakes and omissions!

---

<div class="post-metadata">

**Author:** ![Karl](https://avatars.discourse-cdn.com/v4/letter/k/34f0e0/32.png) [@Karl](https://forums.swift.org/u/Karl)\
**Post date:** [June 26, 2024, 12:56pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/6 "2024-06-26T12:56:21Z")

</div>

> [@glessard](#):
>
> I believe that possibility is marked by the particle "raw" in the type name, just like `UnsafeRawPointer`. There are different types of unsafety and I believe it is useful to use different words for each when possible.

I don't think "raw" quite communicates the severity of the problem - we use "raw" for lots of things that are perfectly safe (the one developers are most likely to be familiar with is `RawRepresentable` and its `rawValue` property).

And yes, there are many ways operations can be unsafe - but the way we have used "unsafe" in Swift has had a consistent meaning; that being:

1. The operation has certain preconditions.

2. Those preconditions are not automatically enforced (at compile-time or run-time) and require manual reasoning by the programmer.

3. Violating the operation's preconditions may cause undefined behaviour.

It has never been limited to issues of lifetimes or bounds-checking.

In this case, I think the operation absolutely fits the bill for being labelled as unsafe. It is essentially the operation which we currently call `unsafeBitCast`, but being exposed on a type whose entire reason for being is that it is supposedly _safe_.

> [@glessard](#):
>
> In the case of `load<T>(as:)`, there is no constraint on the type, and that's significantly more unsafe. We could add a `BitwiseCopyable` constraint on this as well, thereby limiting the fully-unconstrained `load` operation to being used via `UnsafePointer` in a `withUnsafeBytes()` closure.

I don't think `BitwiseCopyable` has any bearing on this. As I described in a recent thread, I don't think it actually provides safety. The other operations could even drop the `BitwiseCopyable` constraint and I don't think they would be any less safe.

> [@MemoryLayout.size of an enum?](https://forums.swift.org/t/memorylayout-size-of-an-enum/71776/13):
>
> I can recall suggestions that `BitwiseCopyable` could allow for these constructs to be written safely - so you could write `<T: RawRepresentable> where T.RawValue: BitwiseCopyable`. I'm not entirely sure that is safe, either.
> 
> For an integer or float, it doesn't matter; it's not a safety issue. You may read an unexpected/nonsense value, but every bit-pattern is defined. If you load an integer or float from some unknown memory address, the compiler obviously won't assume any particular value in a way that UB could result if the assumption was incorrect.
> 
> But for an enum, if you load an enum from random bytes, it may have a bit-pattern that doesn't correspond to any of its cases. So when performing an exhaustive `switch`, it may be that you do not land in any branch:
> 
> ```swift
> enum MyEnum: BitwiseCopyable {
> case a, b, c
> } // size: 1 byte
> 
> let myEnumValue = RandomBytes(1).load(as: MyEnum.self)
> 
> var x: SomeClass
> switch myEnumValue {
> case .a: x = SomeClass(...)
> case .b: x = SomeClass(...)
> case .c: x = SomeClass(...)
> }
> // Is 'x' definitely initialised here?
> // What if myEnumValue is none of a/b/c?
> 
> ```
> 
> I don't think the compiler can/would guarantee that all enum switches land in a deterministic branch, even if the enum has an invalid bit-pattern. At the same time, `MyEnum` is unquestionably `BitwiseCopyable`. So clearly `BitwiseCopyable` isn't the constraint we're looking for.

[Here's an example](https://swift.godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:swift,selection:(endColumn:10,endLineNumber:15,positionColumn:10,positionLineNumber:15,selectionStartColumn:10,selectionStartLineNumber:15,startColumn:10,startLineNumber:15),source:'enum+MyEnum+%7B%0A++++case+a,+b,+c,+d%0A%7D%0A%0A@inline(never)%0Afunc+test(_+input:+MyEnum)+-%3E+Int+%7B%0A++++switch+input+%7B%0A++++case+.a:+return+99%0A++++case+.b:+return+123%0A++++case+.c:+return+24%0A++++case+.d:+return+0%0A++++%7D%0A%7D%0A%0Alet+x+%3D+5%0Alet+invalid+%3D+withUnsafeBytes(of:+x)+%7B+$0.load(as:+MyEnum.self)+%7D%0Aprint(test(invalid))'),l:'5',n:'1',o:'Swift+source+%231',t:'0')),k:38.1475667189953,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:swiftnightly,filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',debugCalls:'1',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'0',trim:'1',verboseDemangling:'0'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:swift,libs:!(),options:'-O',overrides:!(),selection:(endColumn:38,endLineNumber:15,positionColumn:38,positionLineNumber:15,selectionStartColumn:38,selectionStartLineNumber:15,startColumn:38,startLineNumber:15),source:1),l:'5',n:'0',o:'+x86-64+swiftc+nightly+(Editor+%231)',t:'0')),k:39.908174564035214,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:output,i:(compilerName:'x86-64+swiftc+nightly',editorid:1,fontScale:14,fontUsePx:'0',j:1,wrap:'1'),l:'5',n:'0',o:'Output+of+x86-64+swiftc+nightly+(Compiler+%231)',t:'0')),k:21.944258716969493,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4) of this happening. We switch over an enum with 4 cases, which the compiler optimises to a table lookup, assuming it will only ever see those 4 values. By constructing an invalid enum case, we deference past the end of the table. When executing on Godbolt, I find that if the function containing the switch is `@inline(never)` we just read a value from random memory, and if it isn't, it appears to crash (but I don't believe that is guaranteed).

It troubles me that this could be done without any notice at the call site, on a type which claims to be safe. In real code you likely wouldn't see that the loaded value clearly does not correspond to an enum case.

> [@glessard](#):
>
> These are the first non-escapable types to be pitched, but we do not yet know in which order things will land in the standard library. But we need some concrete things to build upon.
> 
> `Span`, `~Escapable` types and `~Copyable` types need to be prototyped and used so that the generalized `Sequence`-like and `Collection`-like don't get designed in a vacuum. Pitching and prototyping the feature also allows the adventurous like you to try things out and point out mistakes and omissions!

For sure - I do appreciate the progress that is being made, and it is exciting. It's particularly nice that there is a working prototype to experiment with.

What I'm saying is that I hope this doesn't progress all the way to a full review and acceptance _before_ we have a clearer picture of what non-copyable collection interfaces will look like. As far as I can tell, there have been no public discussions on that topic, while this is already the second pitch for `Span`.

In other words, I'm saying that in my opinion, there is a preferred order in which things land in the standard library.

---

<div class="post-metadata">

**Author:** ![Nobody1707](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nobody1707/32/2169_2.png) [@Nobody1707](https://forums.swift.org/u/Nobody1707)\
**Post date:** [June 26, 2024, 1:38pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/7 "2024-06-26T13:38:36Z")

</div>

We need an `AnyBitPattern` protocol to constrain these kinds of things to.

---

<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:** [June 26, 2024, 4:06pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/8 "2024-06-26T16:06:58Z")

</div>

I'm not sure there would be a way to enforce the meaning of `AnyBitPattern`; maybe what's needed is to limit it to a small number of standard library types: the floating point types and the integer types.

---

<div class="post-metadata">

**Author:** ![Andrew\_Trick](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/andrew_trick/32/323_2.png) [@Andrew\_Trick](https://forums.swift.org/u/Andrew_Trick)\
**Post date:** [June 26, 2024, 4:13pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/9 "2024-06-26T16:13:36Z")

</div>

> [@Karl](#):
>
> but for me the motivation hasn't been performance - all the code is still generic, and it's `@inlinable` so it _could_ be specialised - instead, the motivation is code size.

We have at least a few critical motivations for `Span`

- guaranteed, predictable performance (including debug builds)
- code size
- supporting library evolution

We need to do everything we can to limit reliance on `@inlinable`.

---

<div class="post-metadata">

**Author:** ![lorentey](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lorentey/32/1299_2.png) [@lorentey](https://forums.swift.org/u/lorentey)\
**Post date:** [June 26, 2024, 5:23pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/10 "2024-06-26T17:23:47Z")

</div>

> [@glessard](#):
>
> `view(as:)` and `loadUnaligned(as:)` are both limited to `BitwiseCopyable` types and, as such, it is possible for them to produce invalid values (that is, values that an initializer would never produce).

I think a crucial question is whether doing that is considered undefined behavior. What semantics does Swift assign to enum instances that do not correspond to any of the enum cases? Do we assign predictable, consistent semantics to their behavior, or not?

If such instances wreak havoc (for example, by violating the compiler's assumptions on switch statements being exhaustive), then we have an issue.

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [June 26, 2024, 5:29pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/11 "2024-06-26T17:29:48Z")

</div>

> [@lorentey](#):
>
> I think a crucial question is whether doing that is considered undefined behavior. What semantics does Swift assign to enum instances that do not correspond to any of the enum cases? Do we assign predictable, consistent semantics to their behavior, or not?
> 
> If such instances wreak havoc (for example, by violating the compiler's assumptions on switch statements being exhaustive), then we have an issue.

It is indeed havoc-wreaking undefined behavior for enums (or even just `Bool`) to be given an invalid representation.

---

<div class="post-metadata">

**Author:** ![nixberg](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nixberg/32/7961_2.png) [@nixberg](https://forums.swift.org/u/nixberg)\
**Post date:** [June 26, 2024, 5:52pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/12 "2024-06-26T17:52:26Z")

</div>

To throw it out there: `ByteDecodable`?

---

<div class="post-metadata">

**Author:** ![Nobody1707](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/nobody1707/32/2169_2.png) [@Nobody1707](https://forums.swift.org/u/Nobody1707)\
**Post date:** [June 26, 2024, 5:57pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/13 "2024-06-26T17:57:32Z")

</div>

There's no way to enforce the semantics of any protocol. If you think it's especially hazardous we could have the conformance automatically inferred with an opt-out, but, honestly, I think enough types have invariants that it should just be completely opt-in.

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [June 26, 2024, 6:13pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/14 "2024-06-26T18:13:44Z")

</div>

> [@Nobody1707](#):
>
> There's no way to enforce the semantics of any protocol. If you think it's especially hazardous we could have the conformance automatically inferred with an opt-out, but, honestly, I think enough types have invariants that it should just be completely opt-in.

For type layout traits, it is possible for the compiler to reason about and enforce them (as it does for `BitwiseCopyable`). It would be theoretically possible to reason about types that are both bitwise-copyable and always valid for any bit pattern since they are transitively made up of `IntNN`, `FloatNN`, etc. (Whether it's important enough to do so, versus letting the developer do what they need to, is a separable question.)

---

<div class="post-metadata">

**Author:** ![lorentey](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lorentey/32/1299_2.png) [@lorentey](https://forums.swift.org/u/lorentey)\
**Post date:** [June 26, 2024, 6:27pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/15 "2024-06-26T18:27:48Z")

</div>

> [@Karl](#):
>
> More broadly, while I support the overall direction, I am very concerned about the lack of a `Collection` interface.

We need to take one step at a time. These are not baby steps -- they are huge leaps, and each of them needs focused attention.

New container patterns are certainly coming, and they will _heavily_ build on non-escapable types. `Span` will play a particularly prominent role in them. We cannot pitch them without having introduced these concepts first.

I fully expect that `Span` will ship alongside the first wave of new container protocols, as a unit.

> [@Karl](#):
>
> I've shown how I use this pattern currently (with generic code), and to me it is very important that this remains something of a low-level implementation detail that does not leak in to interfaces unless contiguous storage specifically is required, and that I can continue to write generic code which works with both `Span`-providing collections and non-contiguous (or non-updated) collections.

It is crucial to note that "`Span`-providing" and "non-contiguous" aren't mutually exclusive concepts. We intend `Span` to be the basic unit of iteration of all borrowing sequences.

At long last, `Span` gives us an opportunity for data structures to directly expose their contents in their actual native form: as _piecewise contiguous series of storage buffers_.

A very significant limitation with the classic `Sequence` is that it insists on iterating over elements one by one. This can induce slowdowns up to a factor of 100x or more for interfaces that take generic sequences. This provides a horrible excuse for API designers to avoid using generics, or to spend effort on trying to manually specialize common cases (like you describe). `Sequence` itself attempts to mitigate this with a ragtag assortment of one-off hooks like [`_copyToContiguousArray`](https://github.com/swiftlang/swift/blob/release/6.0/stdlib/public/core/Sequence.swift#L389-L391), [`_copyContents`](https://github.com/swiftlang/swift/blob/release/6.0/stdlib/public/core/Sequence.swift#L393-L418) or [`_customContainsEquatableElement`](https://github.com/swiftlang/swift/blob/release/6.0/stdlib/public/core/Sequence.swift#L358-L387). The reason these remained forever stuck in the limbo of underscores is that they are hyperfocused on an overly narrow set of cases (and to be honest, they don't do a particularly great job at those, either). The performance issues they patched were important enough that removing them wasn't ever an option, but they are also not general enough solutions to consider making official.

`Span` gives us a safe way to iterate over arbitrary contiguous chunks of storage instead, promising to generally resolve this critical performance bottleneck, especially in unspecialized generic code. For a taste, imagine if `IteratorProtocol` was defined like this:

```swift
protocol IteratorProtocol<Element> {
  associatedtype Element
  mutating func nextChunk(maximumCount: Int) -> depends(self) Span<Element>
}

```

This introduces a two-tiered iteration process: we iterate over native storage chunks, separately iterating over the elements of each. Crucially, the lower layer is operating over `Span`, a tiny, transparent type that can always be specialized -- allowing iteration on that level to proceed at full speed, entirely avoiding dynamic dispatch.

---

<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:** [June 26, 2024, 7:52pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/16 "2024-06-26T19:52:19Z")

</div>

**Bounds checking**

Span and RawSpan have `validateBounds` methods. UTF8Span has equivalent `boundsCheck` methods. Can these share the same name, or be moved to a Range extension?

Span and RawSpan also have `assertValidity` methods, which are currently implemented with a `precondition`. The caller's file and line aren't given.

---

<div class="post-metadata">

**Author:** ![lorentey](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/lorentey/32/1299_2.png) [@lorentey](https://forums.swift.org/u/lorentey)\
**Post date:** [June 26, 2024, 9:53pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/17 "2024-06-26T21:53:42Z")

</div>

Good points!

My beef with the name `validateBounds` is that (1) despite its promise, it is actually validating an index, not the bounds, and (2) it is a predicate, but it doesn't read like one. The latter issue also applies to `UTF8Span.boundsCheck`.

FWIW, in swift-collections, I have recently found a need to introduce similar functionality, and I ended up calling the predicate [`isValid(_: Index)`](https://github.com/apple/swift-collections/blob/main/Sources/RopeModule/Rope/Conformances/Rope%2BCollection.swift#L14). This would not work directly for `Span`s, as they use `Int` as their index -- but `isValidIndex(_: Int)` might be an option to consider. The sibling method for validating a range of indices may perhaps be called `isValidSubrange(_: Range<Int>)`, as that is the term we've established for such ranges.

Note: defining these APIs would set a clear pattern for other types to emulate, but it would probably not be a good idea to make them general requirements of any protocol. Index validation is tricky to do efficiently; it is generally best to do it as an integral part of actual accesses, not as a standalone operation. Very often, the outcome of index validation is not just a yes/no result, but also some direct reference to a part of the underlying container representation that can be directly used to access the addressed item -- a pure `isValidIndex` predicate would discard all this work, only to redo it from scratch when the validated index actually gets used.

(Alternatively, we can of course choose not to provide these members at all -- the swift-collections case wasn't simply about avoiding having to spell out a trivial range check.)

I'd personally prefer to avoid spending API surface on variants that simply assert on the validity of an index -- it raises too many complications:

- Is the client happy to use `precondition` or would they prefer `assert`?
- Inability to customize the runtime error message that the function emits on failure.
- Having to pollute the API signature with #file/#line arguments.

It seems okay to me to let clients invoke `precondition` themselves if that is what they want to do. The swift-collection example also defines `func validate(_: Index)`, and it suggests `validateIndex`/`validateSubrange` for naming. But I regret adding `Rope.validate`; I think it would work better as an internal helper rather than public API. (Luckily, the module that defines it isn't source stable yet.)

---

<div class="post-metadata">

**Author:** ![ST1](https://avatars.discourse-cdn.com/v4/letter/s/e9a140/32.png) [@ST1](https://forums.swift.org/u/ST1)\
**Post date:** [June 27, 2024, 8:52am UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/18 "2024-06-27T08:52:08Z")

</div>

A little offtopic:  
Type `Inout<T: ~Copyable>: ~Copyable, ~Escapable` uses `subscript() -> T` for dereferencing.  
It seems to me that we need a more user-friendly syntax for dereferencing instead of `[]`

---

<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:** [June 28, 2024, 4:53pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/19 "2024-06-28T16:53:23Z")

</div>

I have a few questions:

- In what cases do we expect people to use the unsafe pointer initializers? Presumably, only when they are working with unsafe pointers to begin with, like if they got an unsafe pointer from somebody else, or if they are implementing their own collections on top of unsafe pointers? And everyone else already using (for instance) Array can use Array.storage?
- What are the Law of Exclusivity implications of using spans? Does `borrowing` prevent mutations to the owner binding? (Not fully up-to-date on this)
- Is it correct that the parser example is the motivation for RawSpan, but that parsing utilities are not part of this proposal?

I also wanted to say that I agree with @Karl that `BitwiseCopyable` is not a strong-enough constraint to make `view`, `load` and `loadUnaligned` type-safe because BitwiseCopyable types can have bit patterns that that don't correspond to any value, and whose subsequent use would be undefined behavior. I also agree that "raw" isn't a strong-enough marker to indicate a possibly-havoc-wreaking operation: Raw is also used in "RawRepresentable", and it's not a naughty word there.

Included types include enums (as the case was made here) but also unsafe pointers. There's a few of us who've been privately asking for a stronger constraint (specifying that all bit patterns have a well-defined interpretation) that would resolve this issue. To steal the proposed name, "AnyBitPattern" could match integer types, floating-point types, and aggregates that only contain AnyBitPattern values (including aggregates of aggregates, etc). This category importantly includes many RawRepresentable structures, and on the blessed day that Swift gets fixed-size arrays, it would include those too.

Still, there's a migration path to eventually constrain `view` and friends more (deprecate BitwiseCopyable version, create AnyBitPattern version, create unsafeView/unsafeLoad/etc versions for when you do need to read out something unverifiable; then let people sort out their deprecation warnings), and on balance Span solves urgent problems, so I wouldn't necessarily hold it up for it. I still think that we need to have heightened awareness of these things to avoid accidentally baking unsafe interfaces into primitives that are intended to be safe almost all around. My biggest complaint with UnsafeBufferPointer is that it's easy to over-estimate its safety. It would be best to not create more "safe" primitives whose safety is over-estimated.

---

<div class="post-metadata">

**Author:** ![Joe\_Groff](https://sea2.discourse-cdn.com/swift/user_avatar/forums.swift.org/joe_groff/32/1929_2.png) [@Joe\_Groff](https://forums.swift.org/u/Joe_Groff)\
**Post date:** [June 28, 2024, 5:01pm UTC](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745/20 "2024-06-28T17:01:23Z")

</div>

> [@fclout](#):
>
> What are the Law of Exclusivity implications of using spans? Does `borrowing` prevent mutations to the owner binding? (Not fully up-to-date on this)

Yeah, the memory referenced by a `Span` is borrowed and can't be `inout`-ed while the span is alive.

[Next page](https://forums.swift.org/t/pitch-2-safe-access-to-contiguous-storage/72745.md?page=2)
