SE-0366: Move Function + "Use After Move" Diagnostic

Expanding on that thought, this is the alternative for pitch examples using nesting instead of "move".

The nesting alternative

Example from the pitch:

How we can do it instead:

func test() {
	var x: [Int] = getArray()

	// x is appended to. After this point, we know that x is unique. We want to
	// preserve that property.
	x.append(5)

	// We create a new variable y so we can write an algorithm where we may
	// change the value of y (causing a COW copy of the buffer shared with x).
	
	do {
		var y = x
		longAlgorithmUsing(&y)
		// We no longer use y after this point, so move it when we pass it off to
		// the last use.
		consumeFinalY(y)
	}

	// x will be unique again here.
	x.append(7)
}

Another example from the pitch:

How we can do it instead (pseudocode):

func useX(_ x: SomeClassType) -> () {}
func consumeX(_ x: __owned SomeClassType) -> () {}

func f() {
	let other = do {
		let x = ...
		useX(x)
		return x
	} // x's lifetime ends
	// other is a new binding used to extend the lifetime of x
	useX(other)     // other is used here... no problem.
	consumeX(other) // other is used here... no problem.
}

The second example is more interesting as it asks for the value of "do {...}" block, something which is not currently possible (*):

let other = do {
	let x = ...
	useX(x)
	return x
} // `x` lifetime ended
// `x`  is moved to `other`

We don't have this today (*), but hopefully we will have it at some point (along with other statements like "if" and "switch" used as expressions).

Am I missing something obvious on why move is significantly better than nesting? It feels a bit like "goto" compared to more structured alternatives.

(*) Edit: in fact it is possible today, just using a closure:

let other = { () -> ... in
	let x = ...
	useX(x)
	return x
}() // `x` lifetime ended
// `x`  is moved to `other`

but it would be nice if "do" operator supported it as well (or better! without extra brackets and type annotations).

1 Like