I chewed on this for a long time and wondered when I would ever not want a borrow accessor to be the default behavior (at least for anything more complex than simple POD types that fit in one or two registers), given that it's generally more performant. But then I talked myself into the argument that if I'm writing a computed property, chances are more likely that I'm returning a constructed value or something retrieved from a place that isn't already borrowed. The power here is in being able to write things like wrapper types and other low-level abstractions that just pass through the address without copying (unless the caller needs to).
The limitation that borrow must be paired with mutate instead of yielding mutate feels a bit too restrictive. I can imagine situations where the reading side of an access can be expressed simply as "borrow some existing value" but where the mutation is "mutate some existing value, which can be done by address, but follow that up by post-work to adjust some other internal state". The latter requires yielding mutate instead of mutate, but that forces me to take the overhead of a coroutine to make the read side yielding borrow as well. Breaking that restriction also seems like it would align with the way accessors are synthesized for stored properties today: if I write struct S { var x: String }, the compiler generates a get, a set, and a _modify (the old yielding mutate), but not a _read.
Speaking of stored properties, how do they fit into this picture? The compiler synthesizes get/set/_modify for those today, but what if I want to be able to pass a stored property directly into a borrowing parameter context without incurring any copies—will that just work? (Maybe it does today already?)
The proposal also mentions that borrow accessors can return literals, which makes sense as they have immortal lifetimes. It would be great if we could make this the default behavior for shorthand read-only computed properties where all code paths return a literal. When I write this:
var name: String { "SomeName" }
What I really want is this, but I don't want to have to write the borrow out every time:
var name: String { borrow { "SomeName" } }
(I tried this in whatever the current nightly build is on Godbolt and got a compiler crash in the SIL verifier, I assume that's just a bug in the early implementation? link)
The limitation that borrow/mutate can't borrow from unsafe pointers is also understandable, but unfortunate. Maybe Spans are a better fit here, but can't we just acknowledge that it's unsafe with features we have today? The Future Directions given this example:
Supporting cases like this will require some way to annotate the return expression. For example, we might provide a function-like marker:
var first: Element {
borrow {
return unsafeResultDependsOnSelf(_storage.pointee)
}
}
Could we just express that as this instead:
var first: Element {
borrow {
return unsafe _storage.pointee
}
}
Or is the "depends on self" part load-bearing enough that it can't be expressed with just the unsafe keyword?