Hi Dmitri!
While that's true, I don't think it captures the reasons for our problem. I think what you just described is a characteristic of anything that can be written through a property or subscript. For example, here's a String
property doing that:
struct X { var s = "abcde" }
var x: X
// `String` acts as independent value
var t = x.s
t.removeLast() // doesn't affect x
// `String` acts as “mutable borrow” that passes writes through.
x.s.removeLast()
Making it performant with CoW
When the target of the write is implemented with CoW, getting good performance depends on the property/subscript access not prematurely claiming an additional reference. When the target is already stored in memory somewhere, the compiler happens to oblige us by not claiming that reference, provided the accessor is either:
yield
ing the target's address from a_modify
, or- is the accessor synthesized by the compiler for regular stored properties
The issue for slicing…
…is two^H^Hhreefold: [EDITED]
-
When you slice an array, the
Slice
instance isn't already in memory anywhere, so it has to be constructed on the stack. At least today, that construction eagerly claims an additional reference to the buffer. -
If we could avoid claiming a reference for the
yield
ed slice, when a slice is reshaped:a[3...7].removeLast()
there is code that will deallocate elements belonging to the array if it finds the slice to be uniquely-referenced. I'm not sure how much of that code is compiled into clients already, but that could be a problem if the only test is uniqueness.
-
If you could avoid claiming a reference for the yielded slice, you'd still need to prevent the array buffer from being deallocated in situations like this
extension ArraySlice { mutating func reassign() { self = ArraySlice() } } a[3...7].reassign()