`borrow` and `take` parameter ownership modifiers

I'm still not really sold on the name take, to be honest. I'm sorry to keep harping on about terminology, but we only get one chance to ship this feature and I think how approachable it ends up being will largely be decided by how intuitively the code reads.

I had some suggestions in the previous review. Even if we decide something else, I still think it's worth brainstorming this a little more. Personally, I don't think take is an improvement over move or consume.

That aside, and just taking the proposal as it currently is, if a method takes self (sounds weird, right? What I mean is that it consumes self via the take operation... you get it), we would write that function as:

taking func foo()

But parameters are written with plain take (no 'ing'):

func foo(_: take String)

I think it would read better for parameters to also be written taking. It's still not perfect but I think it's better than plain take.

func foo(_: taking String)
func append(contentsOf: take some Sequence<Element>)
func append(contentsOf: taking some Sequence<Element>)

More broadly, the proposal mentions one motivation for using take like this is so we can use the same keyword in the function declaration and at the call-site. I'm not so sure that kind of simplicity necessarily leads to a more intuitive design.

  • As noted above, the proposal would already introduce a separate keyword for functions that consume self.

  • Function declaration are annotated throws, but errors are returned with throw (no 's') and calling a throwing function requires the keyword try at the call-site.

    Most people seem to understand that well, I think? I've never heard of anybody asking to annotate function declarations with try or to call a throwing function with throw.

  • async functions similarly require the keyword await at the call-site.

So I don't think we should necessarily be afraid of using different keywords in different contexts if it makes the overall design easier to read. I really do think that is going to determine how comfortable developers feel using this feature and how often they use it correctly.

Ownership is seriously important. It deserves as many keywords as it needs to get the right design, IMO.

Is it, though? One pattern in my own code that I have found would benefit from these annotations are lazy collection views - I like to write methods which calculate indexes as initialisers on the index type itself (i.e. "calculate the next index from this position in this source collection"). That's just how I like to write it. But since parameters to an initialiser are implicitly take, this leads to additional retain/release overheads which can be significant. In order to avoid those overheads, I would need the features being proposed here - to explicitly specify that the source is a borrow parameter and isn't being consumed.

Is that systems programming? Not really, I don't think. That's fairly regular code that you could see in any Swift app, and that's why I think it's important that these features are designed in a way that is approachable to all Swift developers.

3 Likes