[Pitch] Borrowing Accessors

One thing I am sometimes confused about with the existing borrowing/inout SomeNonCopyableType parameter bindings, which is now pushed further by these accessors: How are we intended to think about the “types” returned by a borrow/mutate accessor (I guess same question for the type yielded by the coroutine ones). I think this question is heavily related to the pitch from here Pitch: `borrow` and `inout` declaration keywords .

Right now in the language working with accessors returning borrowed values can require either painful ergonomics or inefficient code that relies on repeated expression elimination which wouldn’t be possible across resiliency boundaries. The common case is if I want to do more than one thing with a borrowed value without re-creating the borrow.

For example:

struct Node: ~Copyable {
    ...
}
struct Tree: ~Copyable {

  var leaf: Node {
    borrow {
      // Do some sort of traversal to find first leaf and return a borrow of it.
    }
  }
}

func leafUseOne(_ node: borrowing Node) { ... }
func leafUseTwo(_ node: borrowing Node) { ... }

func main() {
  var tree: Tree = ...

  // I want to feed the first leaf to `leafUseOne`, then to `leafUseTwo`
  // Today I have to re-write:
  leafUseOne(tree.leaf)
  leafUseTwo(tree.leaf)

  // Or I could do this which is more efficient but a bit ergonomically painful
  { (leaf: borrowing Node) -> Void in
    leafUseOne(leaf)
    leafUseTwo(leaf)
  }(tree.leaf)
}

Maybe the above example is contrived, and you could argue something that has to do a traversal shouldn’t be a property in the first place, but anyway… Since there is no way to store a borrow in a local variable, I either have to invoke the accessor twice, or to guarantee it doesn’t redo the work, I’d have to define a separate closure to do it (I’ve been working with noncopyable types a good amount recently and am having to do quite a bit of this ‘closure strategy’ which hurts readability).

Is there any potential for including a future direction where there’s some unification around what a `borrowing T` as parameter, and borrow accessor return value, represent as a type? Will these things be merged into some concrete type in the future that can be assigned to a local variable? Or is the answer that borrowing T itself is a type, and that is the type returned by a borrow accessor, but just not yet supported to bind to a local variable? For example will I eventually be able to write let leaf: borrowing Node = tree.leaf. I think this question is important especially as we evaluate the usability of these accessors across module boundaries.

Also further question thinking along the same lines, if there is an eventual type borrowing T, then would var x: T { borrow } actually just be a shorthand for var x: borrowing T { get }? Regardless of being able to assign to a local variable, the future directions already mentions being able to return a borrowing T from a function, so there’s a difference where for a function you change it to return a borrowing T but for properties you don’t change the return type, but rather the accessor type.

3 Likes