The new Span
type is described in the docs as iOS 12.2+, macOS 10.14.4+, …
However, every API I could find thus far that vends a Span
(like Array
) is appleOS 26.0+.
Is there a way to create a Span
on older Apple OS versions?
The new Span
type is described in the docs as iOS 12.2+, macOS 10.14.4+, …
However, every API I could find thus far that vends a Span
(like Array
) is appleOS 26.0+.
Is there a way to create a Span
on older Apple OS versions?
It should be possible for most of the new standard library APIs that produce Span
to take advantage of Span
's back deployment library. I let the team know that they can lower the minimum OS requirement for these APIs.
The Span
types themselves are (planned to be) available before Swift 6.2, but the properties on Array
and String
may also depend on bridging changes in the stdlib and Foundation that may not be backdeployable.
Cc @glessard
Array.span
, String.utf8.span
and Substring.utf8.span
aren't back-deployable due to the bridging changes they depend on.
Thank you all for looking into this! In my case, I would want to parse binary data byte by byte, so Span<UInt8>
and RawSpan
would both be suitable. Currently, I am using UnsafeBufferPointer
to avoid the bounds checks (redundant, since my parser checks each byte, too).[1]
So, both UnsafeBufferPointer.span
and UnsafeRawBufferPointer.bytes
would work for me, but are currently appleOS 26+. Or perhaps ContiguousArray.span
would not be affected by Foundation bridging (also 26+ currently)?
Too bad about String.utf8.span
being blocked, I would have loved to use that, too. Perhaps this is something that could be solved by a non-bridging string, like ContiguousString
? Probably not in time for my use case, but to work around such bridging-blocks in the future.
I know, Span
does bounds checks as well, but it allows to opt out of checks on a per-access basis. ↩︎
You can use the existing withUnsafeBufferPointer
APIs to provide a partial polyfill -- however, the performance characteristics of that are too much of a trap to wholeheartedly recommend doing that. (And writing a withSpan
wrapper around withUBP
requires Span
initializers which IIRC we do not have yet. When they arrive, they are expected to be backdeployable.)
A crucial part of the span
properties is that they are borrowing storage that is owned by and physically exists somewhere in the instance -- so repeated span
invocations do not come with endlessly repeating, huge bridging/etc overhead. (Some bridged NSArray/NSString instances don't use/expose contiguous storage, and for those a contiguous buffer sometimes needs to be allocated on the first invocation of span
; but the buffer is then saved and transparently reused, so the properties have "amortized O(1)" complexity (in the way the stdlib uses that term).)
The old withUBP
functions are of constant complexity on some instances, and linear on others: when necessary, they materialize contiguous storage in a temporary buffer, which they immediately throw away at the end of the call. This unpredictable/unreliable behavior makes them generally unusable (or, at best, tricky to use) in the performance-sensitive use cases that the unsafe buffer pointer types are supposed to enable: they are common sources of accidentally quadratic behavior and similar problems. The span
properties are designed to avoid this -- but this unfortunately does have the (temporary!) cost of availability pain.
(We are playing a long game. As usual, some people will be able to immediately make use of the new facilities, why others are sadly forced to wait a while. Eventually Swift will still gain reliable, predictable, efficient primitives for everyone; it just takes a bit longer for some to start using them.)