I feel like the compiler's rules for when to consume and borrow are confusing and arbitrary. This even extends to the language itself, like if let potentially being borrowing as a future direction in SE-0390, but let bindings always being consuming, except if the left-hand side is _.
I made a suggestion a while ago to fix this, which is admittedly kind of unpolished, but I do hope that in general we can get a more robust/precisely-defined system of ownership inference at some point.
It's also worth noting that the property access being consuming seems to contradict SE-0390:
A class or noncopyable struct may declare stored let or var properties of noncopyable type. A noncopyable let stored property may only be borrowed, whereas a var stored property may be both borrowed and mutated. Stored properties cannot generally be consumed because doing so would leave the containing aggregate in an invalid state.
Looking at SE-0390, it also seems that the ownership of nonescaping closure captures is dependent on whether the binding is declared as let or var, regardless of how the closure actually uses the binding. So your closure workaround might be working for entirely unintuitive reasons:
Capturing an immutable local binding into a nonescaping closure borrows the binding for the duration of the callee that receives the nonescaping closure.
Capturing a mutable local binding into a nonescaping closure is a mutating use of the binding for the duration of the callee that receives the nonescaping closure.
Accessing a property should not be considered consuming (and indeed can't be yet because we don't yet support partial consumption). That's a bug. How is etag declared—is it a stored property? Does it have a consuming get?
Also, remember that you can just use copy to bypass possibly-spurious consumption errors, like:
it was just a regular stored property. computed properties, and properties that witness protocol requirements don’t have this problem for some reason.
well that only works if Self:Copyable. a lot of my experience in the past two months or so has been sinking a lot of effort into adopting ~Copyable and then having to backtrack.
In that case, I think let etag = copy (borrow self).etag should work (awfully wordy, I know). Although stored property getters should already default to borrowing.
I can't seem to write a reproducer for this. Do you have a more self-contained bit of code that causes stored property access to consume self?
i have never heard of such a keyword borrow, and when i tried writing it in the position (borrow self), sourcekit-lsp failed to parse the expression. is borrow a nightly-only thing?
i also had a hard time reproducing it, which drove me nuts because i could have sworn i had run into this exact error many times. after a some experimentation, i discovered that the implicit consume behavior is actually different if Self:~Copyable:
struct S1
{
let a:[Int]
let b:[Int]
// error: 'self' consumed more than once
consuming
func f() -> [Int]
{
var a:[Int] = self.a
a += self.b
return a
}
}
struct S2:~Copyable
{
let a:[Int]
let b:[Int]
// Works fine.
consuming
func f() -> [Int]
{
var a:[Int] = self.a
a += self.b
return a
}
}
i guess this is one of the things flies right over your head while you’re stumbling around adding and deleting keywords to try and get this stuff to work…
I could have sworn they added a keyword to borrow a value when they added copy and consume, but it seems like that must be a figment of my imagination.
There are plans to introduce a borrow operator, which would suppress implicit copies when accessing shared-mutable properties such as globals or class ivars. It hasn't yet been proposed or implemented yet.
it would be nice if these new keywords were documented somewhere. we are nearly three quarters the way to 5.10 and a google search for swift copy keyword still returns no useful results.
currently trying to work around this by replacing consuming with __consuming; from the perspective of the optimizer, is there any difference between satisfying a consuming protocol requirement with a real consuming method or a __consuming method?
To callers, consuming and __consuming are the same. They do have different mangling behavior akin to consuming/__owned, so if you're working on code that has to be ABI stable, using __consuming now may mean you're stuck with it forever. The __ variants only continue to exist to support existing code that can't change.