I find that it's OK to access first and last properties of an array after it has been consumed. Is it a bug or a special feature? Acessing the array's count property or subscript produce errors as expected.
func test() {
let values = [1, 2, 3]
let newValues = consume values
newValues.append(4)
//print(values.count) // this doesn't compile (as expected)
//print(values[0]) // this doesn't compile (as expected)
print(values.first!) // output: 1
print(values.last!) // output: 3
}
Generalizing your example a bit, I find it's related to whether the matched method is from a protocol extension:
protocol P {
func bar()
}
extension P {
func bar() {}
}
struct A: P {
let x: String = ""
func foo() {}
}
func test() {
let a = A()
_ = consume a
a.foo() // error, as expected
a.bar() // no error, not as expected
}
Thanks. I filed #83277. It's a bit hard for me to understand how a feature available since Swift 5.9 still has a so basic issue. I started reading ownership related documents yesterday and found the issue soon. Sometime I can't help wondering if it's because Swift has become so complex that it's hard to maintain it or if it's because few people actually use these features.
it seems it's not just protocol extensions, but generic functions of various flavors that exhibit this behavior (which is what protocol extensions in some sense are). the behavior also differs between local variable bindings that are consumed, and parameter bindings with consuming ownership, so perhaps there is some implicit copying issue at play (godbolt).
func consumeValue<V>(_ v: consuming V) { _ = consume v }
func borrowValue<V>(_ v: borrowing V) { _ = v }
func useValue<V>(_ v: V) { _ = v }
func consumeA(_ a: consuming A) { _ = consume a }
func borrowA(_ a: borrowing A) { _ = a }
func useA(_ a: A) { _ = a }
struct A {
var prop = ""
}
func testLocalBindingUseAfterConsumeGenerics() {
let a = A()
_ = consume a
// 👇 replacing the `_ = consume a` with this will cause no error
// but if they are both present, the second consume will error
// consumeValue(a)
borrowValue(a)
useValue(a)
}
func testLocalBindingUseAfterConsumeNoGenericFns() {
let a = A() // error: 'a' used after consume
_ = consume a // note: consumed here
// without the generic indirection, these all error:
consumeA(a) // note: used here
borrowA(a) // note: used here
useA(a) // note: used here
}
func testParameterBinding(_ a: consuming A) { // error: 'a' used after consume
_ = consume a // note: consumed here
// consumeValue(a) // consuming like this will also cause an error
borrowValue(a) // note: used here
useValue(a)
}