Consider:
struct Vector<T> {
var scalars: [T]
static func +=(lhs: inout Vector, rhs: Vector) {
for i in lhs.scalars.indices { lhs.scalars[i] += rhs.scalars[i] }
}
}
This implementation is suboptimal in the case where lhs.scalars
shares its storage with another array, because it first copies the scalars in lhs
into new storage and then writes over them, accumulating elements from rhs
into it. What we really want to be able to do is:
struct Vector<T> {
var scalars: [T]
static func +=(lhs: inout Vector, rhs: Vector) {
if hasUniquelyReferencedStorage(&lhs.scalars) {
for i in lhs.scalars.indices { lhs.scalars[i] += rhs.scalars[i] }
}
else {
lhs = lhs + rhs // build new storage once and replace old storage with it
}
}
}
Currently the only known way to do the check for unique storage is a horrible hack that probably violates the memory model:
extension ObjectIdentifier {
/// Returns true iff the object identified by `self` is uniquely referenced.
///
/// - Requires: the object identified by `self` exists.
/// - Note: will only work when called from a mutating method
internal func _liveObjectIsUniquelyReferenced() -> Bool {
var me = self
return withUnsafeMutablePointer(to: &me) {
$0.withMemoryRebound(to: AnyObject.self, capacity: 1) {
isKnownUniquelyReferenced(&$0.pointee)
}
}
}
}
/// Returns `true` iff the reference contained in `x` is uniquely referenced.
///
/// - Requires: `T` contains exactly one reference or optional reference
/// and no other stored properties or is itself a reference.
func unsafeHasUniquelyReferencedStorage<T>(_ x: inout T) -> Bool {
unsafeBitCast(x, to: ObjectIdentifier.self)._liveObjectIsUniquelyReferenced()
}
Ideally, we would have a way to ask generically whether all the references in any given type were uniquely-referenced, but as a start, it would be extremely useful just to be able to ask that question about arrays, sets, and dictionaries. My pitch to you is that we expose unsafeHasUniquelyReferencedStorage
in the standard library.
The alternative, for those unwilling to use horrible hacks, is to reconstruct all the functionality of the standard library collections on top of ManagedBuffer
et. al just so that one is able to access the underlying reference and ask the uniqueness question, and even then, you have a type that doesn't interoperate with Array
et. al.
Q: Why not a first-class safe property on Array, Dictionary, Set, and String? A: we could do that, but there are two reasons I pitched the unsafe API. First, this extremely low-level property would clutter the API of these centrally-important types. Second, the unsafe API is more broadly useful. That said, I'm not attached to the form of the answer and would be happy to propose a different variant if consensus dictates that.