FWIW, I loathe this precondition, too. It originates from Sequence._copyContents, in a rather unfortunate example of a half-finished, temporary interface polluting the stdlib’s public API. (I hate almost everything about _copyContents, too.)
The issue with relaxing this requirement is that code targeting the new implementation won’t be safe to back-deploy to earlier Stdlib releases, unless we can somehow ensure that newly built code will never call the preexisting symbol.
For example, we could do this with the moral equivalent of:
// Original entry point
@available(unavailable, *)
public func initialize<S: Sequence>(from source: S) -> (S.Iterator, Index)
where S.Element == Element {
initialize(from: source, _hiddenDistinguishingMarker: ())
}
// Back-deployable replacement
@_alwaysEmitIntoClient
public func initialize<S: Sequence>(
from source: S,
_hiddenDistinguishingMarker: Void = ()
) -> (S.Iterator, Index)
where S.Element == Element
This feels too disgusting to consider. (Although perhaps we could implement some language enhancements to make it a little more palatable — or even more disgusting, depending on your viewpoint.
)
Unfortunately we’d have to do the same for _copyContents (which is where the requirement originates from), which probably makes this approach impractical, even if we were otherwise okay with such API hacks. Changing the preconditions of a protocol requirement (even an undocumented one) is a Big Deal — it’d be a source-breaking change.
Another approach is to document the first versions of initalize(from:) and _copyContents that implemented the relaxed precondition, and hope that people will keep this information in mind. This would be extremely cheap to implement, but I think it would be too user-hostile to be acceptable: it would effectively require clients to implement runtime availability checks, without the benefit of static compiler diagnostics that indicate this
The third option is to ignore the stdlib and write your own extension method that works the way you want. Sadly this is only practical if the parameter is a concrete type whose storage layout you are able to directly access. This is often the case, but this doesn’t make this a satisfying answer.
Probably the best option is to replace this API with something better. This is best done at the same time as we introduce a public replacement for _copyContents, with a more flexible design. My current thinking is that the _copyContent replacement would work best as a mutating IteratorProtocol method that provides read-only access to piecewise contiguous storage chunks of a client-supplied maximum size. I.e., something like:
protocol IteratorProtocol {
mutating func next() -> Element?
@available(…)
mutating func withNextChunk<R>(
maximumSize: Int? = nil,
body: (UnafeBufferPointer<Element>) throws -> R
) rethrows -> R
}
(Or, given the popularity of IndexingIterator, perhaps a static Sequence method that takes an inout iterator. There are many possible variants of this — another approach would be to replace the maximumSize parameter with the closure indicating how many elements it was able to process from the chunk it was given, or to have the caller pass in an optional temp buffer, NSFastEnumeration-style.) This method can then be used to build/implement higher-level APIs that efficiently copy data across (piecewise, or fully) contiguous collections, including a more flexible UMBP.initalize(from:).
This is likely best done once we have several good examples of piecewise contiguous data structures to play with — for example, we have none in the stdlib, and in Swift Collections right now we only have Deque. When we land the B-tree based SortedSet, then we might have enough guinea pigs to experiment on to design a proper API.
The big drawback to this is that new Collection or IteratorProtocol requirements would need to come with availability, which complicates their use from existing stdlib APIs that currently call _copyContents. (I doubt making this particular niche little stdlib feature back deployable would be worth the effort required.)