Ownership inference?

Something I've thought about with SE-0390 is that the distinction between "consuming operations" and "borrowing operations" feels rather arbitrary. For example, if let optional bindings are left open to being borrow-by-default, but let bindings are being taken to mean move-only bindings. But as another special case, _ = value bindings are borrowing.

Currently, function arguments elide explicit consume and borrow keywords at the call site. Something I think might be a good idea is for this elision to be generalized beyond function arguments, in a way analogous to type inference, so that the syntax of explicit consume and borrow operators "melts away" in most cases.

This would allow Swift programmers the ability to use idiomatic patterns, such as using temporary let bindings to access properties or elements of an array, in a way that feels familiar to how they work with copyable values currently, but without the burden of choosing between consume and borrow bindings. This would be similar to how inferring ownership for values passed to functions avoids the same burden.

Examples:

let x = someStruct.someProperty // Inferred to be borrowing or consuming
                                // depending on whether someFunction
                                // consumes or borrows its argument
someFunction(x)
let x = FileDescriptor() // Inferred to be consuming
                         // because x is later consumed by y
let y = x // Inferred to be consuming because y is later consumed by use()
use(y)
// Ownership can't be inferred in function signatures,
// for the same reason that types can't be inferred.
func useFileDescriptor(_ x: borrowing FileDescriptor) {
    let y = x // Inferred to be consuming because y is later consumed,
              // and therefore invalid, because x is borrowed
    use(y)
}
// x's ownership is inferred based on fileDescriptor's ownership,
// which in turn is inferred based on whether or not someFunction
// consumes or borrows its argument.
switch x {
case .firstCase(let fileDescriptor):
    someFunction(fileDescriptor)
}

This would also allow interesting future directions. For example, suppose we allow ownership to become generic, so that, for example, tuples can either own or borrow each of their fields. We can then allow something like this:

let x = FileDescriptor()
let y = (3, x) // y is inferred by default as
               // (Int, borrowing FileDescriptor)
use(y.1) // However, this line would cause y to be
         // inferred as (Int, consuming FileDescriptor)
3 Likes