if var
if var P = place binds a pattern into the place itself, exactly like a var parameter, instead of copying the value out first.
Diese Seite gibt es noch nicht auf Deutsch, deshalb steht sie hier auf Englisch. Zur englischen Seite
Every other pattern binds a copy: if const Some(user) = current gives the body a user that is its own value, and
changing it changes nothing current holds. if var is the one exception, for the one case where that copy is the
problem.
Example#
type Counter {
var count: Int = 0
var fn increment() {
count = count + 1
}
}
fn run() {
var current: Counter? = Counter()
if var Some(counter) = current {
counter.increment()
}
print current
}
run()Syntax#
if var <pattern> = <place> { ... }
while var <pattern> = <place> { ... }Rules#
if var P = placebinds the names ofPintoplaceitself, not into a copy of whatplaceholds. Inside the body, a name the pattern bound is avarpath back to that part ofplace, exactly as avarparameter is a path back to the caller's argument.The subject has to be a
varpath. Everything Mutation and var paths allows as the argument of avarparameter is allowed here: avarbinding, a field of one, an index, a slice. Aconstbinding, a temporary, or the result of a call is not avarpath, and none of them can stand afterif var.The body runs inside a
varaccess to the matched part ofplace. A method that is avar fn, such asincrementabove, can be called on the bound name precisely because the pattern reached it through avarpath and not through a copy.A pattern that only binds names benefits from
if var; a pattern that only reads does not need it.if varexists so thatif const Some(iterator) = current { iterator.next() }can change the very iteratorcurrentholds - not to make ordinary reads faster, and not to change what the pattern itself matches.
What this is not#
if var is not a second syntax for the same thing if const does. The two differ in exactly one respect: what a
bound name is a path to. Reading through either one looks identical; only a write through the name shows the
difference, which is why picking the wrong one is easy to miss until a write silently lands on a copy.
type Counter {
var count: Int = 0
var fn increment() {
count = count + 1
}
}
fn run() {
var current: Counter? = Counter()
if var Some(counter) = current {
counter.increment()
}
print current
}
run()type Counter {
var count: Int = 0
var fn increment() {
count = count + 1
}
}
fn run() {
var current: Counter? = Counter()
if const Some(counter) = current {
var mutableCopy = counter
mutableCopy.increment()
print mutableCopy
}
print current
}
run()The second program type checks: counter under if const is a const name, so it has to be copied into a var
binding before anything can call increment on it - and that call changes the copy, not current. if var skips the
copy instead of requiring one.
if var is not a reference that outlives the if. The bound name is a var path only for the body, the same
rule that makes a var parameter second-class: it cannot be stored in a field, returned, or captured by a closure
that escapes the block.
Related#
- Mutation and var paths - what counts as a
varpath anywhere in the language. - Patterns in bindings and conditions -
if constandwhile const, the copying siblings. - Why values instead of references - why a
varpath exists only for the duration of a call or a block, and never longer.