We are proposing OutputSpan
a new abstraction for initialization of memory, along the lines of Span
and MutableSpan
. OutputSpan
will allow us to replace unsafe in-place initialization API such as Array.init(unsafeUninitializedCapacity:initializingWith:)
.
OutputSpan
& OutputRawSpan
Introduction
Following the introduction of Span
and MutableSpan
, this proposal adds a general facility for initialization of exclusively-borrowed memory with the OutputSpan
and OutputRawSpan
types. The memory represented by OutputSpan
consists of a number of initialized elements, followed by uninitialized memory. The operations of OutputSpan
can change the number of initialized elements in memory, unlike MutableSpan
which always represents memory that is initialized.
Motivation
Some standard library container types can delegate initialization of some or all of their storage to user code. Up to now, it has only been possible to do so with explicitly unsafe ways, which have also proven error-prone. The standard library provides this unsafe functionality with the closure-taking initializers Array.init(unsafeUninitializedCapacity:initializingWith:)
and String.init(unsafeUninitializedCapacity:initializingUTF8With:)
.
These functions have a few different drawbacks, most prominently their reliance on unsafe types, which makes them unpalatable in security-conscious environments. We continue addressing these issues with OutputSpan
and OutputRawSpan
, new non-copyable and non-escapable types that manage initialization of typed and untyped memory.
In addition to the new types, we will propose adding new API for some standard library types to take advantage of OutputSpan
and OutputRawSpan
, and improve upon the Array
and String
initializers mentioned above.
Proposed solution
OutputSpan
OutputSpan
allows delegating the initialization of a type's memory, by providing access to an exclusively-borrowed view of a range of contiguous memory. OutputSpan
's contiguous memory consists of a prefix of initialized memory, followed by a suffix of uninitialized memory. Like MutableSpan
, OutputSpan
relies on two guarantees: (a) that it has exclusive access to the range of memory it represents, and (b) that the memory locations it represents will remain valid for the duration of the access. These guarantee data race safety and temporal safety. OutputSpan
performs bounds-checking on every access to preserve spatial safety.
An OutputSpan
provided by a container represents a mutation of that container, and is therefore an exclusive access.
OutputRawSpan
OutputRawSpan
allows delegating the initialization of heterogeneously-typed memory, such as memory being prepared by an encoder. It makes the same safety guarantees as OutputSpan
.
Extensions to standard library types
The standard library will provide new container initializers that delegate to an OutputSpan
. Delegated initialization generally requires a container to perform some operations after the initialization has happened. In the case of Array
this is simply noting the number of initialized elements; in the case of String
this consists of validating the input. This post-processing implies the need for a scope, and we believe that scope is best represented by a closure. The Array
initializer will be as follows:
extension Array {
public init<E: Error>(
capacity: Int,
initializingWith: (_ span: inout OutputSpan<Element>) throws(E) -> Void
) throws(E)
}
We will also extend String
, UnicodeScalarView
and InlineArray
with similar initializers, and add append-in-place operations where appropriate.
Please see the full proposal for the detailed design.