Maybe the passing convention for noncopyable types should default to borrowing

My thesis is simple:

  1. Writing Swift code that supports noncopyable types is already pretty verbose
  2. Pass-by-value in Swift primarily means “I want to read this thing.” The fact that you might also copy it is really an artifact of Swift's legacy that everything is Copyable.
  3. The same code operating on Copyable types would have the same semantics without any explicit borrowing.

Code to support noncopyable types would read much more naturally if you didn't have to be explicit about borrowing, IMO.

7 Likes

I’m a bit torn on whether I think this is a good idea universally, still thinking it through. But for non-public APIs I see very little downside!

2 Likes

What are your reservations, and why does public-ness have any bearing on the choice?

1 Like

Anything implicit becomes the convention someone will use without thinking about it. If the default is consuming (like Rust), you may or may not find out in your test cases, but worst case you can always make a new borrowing API and have the consuming one forward to it. If the default is borrowing and you happen not to need to consume the item today, you may not realize it was supposed to be a consuming API all along. Then you ship it, then you realize, and now it’s a source-breaking change (or worse, binary-breaking), if you can’t contort your new implementation into working on borrowed values.

(And today’s explicit model requires you to think about it up front, at the cost of making declarations even longer, which I agree is a speed bump in readability.)

I don’t think this kind of mistake will be common, but how uncommon it is I’m not sure, and especially when binary compatibility is in play the cost of getting it wrong is serious. Requiring public APIs to be explicitly annotated forces someone to think about it at least a little, just like how we don’t make public the default in the first place. We can decide whether or not that’s worth the verbosity, but meanwhile we could also add it for non-public arguments and get that benefit immediately.

2 Likes

All the same considerations apply to passing copyable types though. What makes noncopyable types special?

With a copyable type, if it’s accidentally borrowing instead of consuming, you can make a copy. If it’s accidentally consuming instead of borrowing, you can borrow from the unwanted copy. This is a performance issue, but it doesn’t limit what you can do with the value.

It does if you eventually realize you needed the convention to be inout. Maybe that's less likely, I don't know, but it can only be a matter of degree.

2 Likes