It it possible to specify a returned non-copyable value as borrowed?

It seems like I can't do this right now:

struct SomeValue: ~Copyable { }

func doSomething() -> borrowing SomeValue { }
var doSomething: borrowing SomeValue { }

On the other hand, stored properties have do have this quality out of the box, and are implicitly borrow-only:

struct A: ~Copyable { }

struct B: ~Copyable {
    
    let stored = A()
    
    var computed: A { A() }
    
}

func hello(b: borrowing B) {
    let y = b.stored // disallowed: 'b' is borrowed and cannot be consumed
    let z = b.computed // allowed
}

Am I missing something? or is my only option basically:

struct B: ~Copyable {
    
    func borrowAnA(_ closure: (borrowing A) -> Void) {
        //  ...
    }
    
}
2 Likes

i don't have much 'real world' experience with noncopyable types, so i may be off here, but i think if you can tolerate the use of underscored (yet longstanding) stdlib APIs, or experimental features, then this can perhaps be achieved by use of the _read/read coroutine accessor.

e.g. if i modify your code as so:

struct B: ~Copyable {
    
    let stored = A()
    
    var computed: A { 
        _read { yield A() }
    }
    
}

then i get the error:

13 | func hello(b: borrowing B) {
14 |     let y = b.stored // disallowed: 'b' is borrowed and cannot be consumed
15 |     let z = b.computed // allowed
   |               |- error: 'b.computed' is borrowed and cannot be consumed
   |               `- note: consumed here
16 | }
17 | 
2 Likes

Wow I didn't know this existed (I guess that's why they're underscored lol). I'd like to avoid experimental/non-standard features for now, given that I'm working on code that will be in a public library... but it's good to know that technically this is an option.

2 Likes

Here's a fun workaround I just found. Non-copyable values returned from subscripts and properties defined in a protocol are automatically treated as borrow-only, so a protocol (though introducing some indirection) gives an alternative, until the tools are available for explicitly specifying a value's ownership:

struct NonCopyableValue: ~Copyable { }

protocol ContainerProtocol: ~Copyable {
    
    var noncopyable: NonCopyableValue { get }
    
    subscript(_ i: Int) -> NonCopyableValue { get }
    
    func getNoncopyable() -> NonCopyableValue
    
}

struct Container: ContainerProtocol, ~Copyable {
    
    func getNoncopyable() -> NonCopyableValue {
        fatalError()
    }
    
    subscript(i: Int) -> NonCopyableValue {
        fatalError()
    }
    
    var noncopyable: NonCopyableValue {
        fatalError()
    }
    
}

func doSomething(with container: borrowing some ContainerProtocol & ~Copyable) {
    let x = container.getNoncopyable() // allowed
    let y = container[0] // disallowed
    let z = container.noncopyable // disallowed
}

doSomething(with: Container())