#Values And References
Status: language specification draft.
This chapter defines reference types and reference expressions: their syntax, their typing, and what they do at run time. A reference type is a counted reference type, *T or *mut T, or a local reference type, &T or &mut T. Its values refer to a T that lives elsewhere.
*T and &T read the T they refer to, and *mut T and &mut T also write it. A counted reference may be stored, returned, and captured. A local reference is lent to a call and never kept past it.
A reference to a variable or a field shares that place. A reference to any other value refers to a new object that holds a copy of it.
struct Body:
pos: Point
vel: Point
fn drift(body: &mut Body, step: &Point) -> void:
body.pos = *step
fn spawn(pos: Point) -> *mut Body:
&mut Body { pos: pos, vel: Point { x: 0, y: 0 } }#Reference Types
A reference type is *, *mut, &, or &mut followed by a type.
fn first(items: List[*Point]) -> Option[*mut Point]:
.None
fn each(f: fn(&Point) -> *mut Point, pending: *Point?) -> void: pass- ref.type.forms
&Tis a local reference type, and&mut Tis a writable local reference type. The operandTis a named,dyn, tuple, or grouped type. - ref.type.counted-forms
*Tis a counted reference type, and*mut Tis a writable counted reference type. Their operand is as for&T. - ref.type.kindsCounted and local are the two kinds of reference. A rule of this chapter that says "a reference" holds for both kinds.
- ref.type.kinds.elsewhereA rule in another chapter that writes
&Tor&mut Tfor a reference that is read, written, matched, compared, or printed holds for*Tand*mut Ttoo. This rule is temporary, as the note below says. - ref.type.kind-runtimeA local reference is counted at run time, as a counted reference is. Only the rules of Local References tell the two kinds apart.
- ref.type.positionA counted reference type may appear wherever a type may: a parameter, a result, a field, a binding annotation, a type argument, and a function type's parameters and result.
- ref.type.optional
?applies to the whole reference type:*T?is an optional*T. - ref.type.groupTo refer to an optional or to another reference, group the operand:
&(T?)and&(&T). - ref.type.no-nesting
&&Tis a syntax error, because&&is one token. So are&mut mut Tandmut &T. Error:syntax-error. - ref.type.counted-no-nesting
**Tis a syntax error too, because**is one token, so a counted reference to one is written*(*T). So are*mut mut Tandmut *T. Error:syntax-error. - ref.type.no-function
&never directly precedesfn:&fn() -> i32and&mut fn() -> i32are errors. Error:syntax-error. - ref.type.counted-no-function
*never directly precedesfneither:*fn() -> i32and*mut fn() -> i32are errors. Error:syntax-error. - ref.type.distinct
&Tand&mut Tare distinct types. Each is invariant inT:&Ais&Bonly whenAisB. - ref.type.kind-distinct
*Tand*mut Tare distinct types, and distinct from&Tand&mut T. Each is invariant inT. - ref.type.any-refA reference type implements
AnyRef. - ref.type.dyn
&dyn Trand&mut dyn Trare trait value references, whose conversions and members Trait Value References states. - ref.type.dyn-counted
*dyn Trand*mut dyn Trare counted trait value references. They take the same conversions and members, with*in place of&on both sides of each conversion.
Note.
ref.type.kinds.elsewherebridges the chapters until the respelling slices of #834 spell each such rule for both kinds. Those slices then remove it.
fn bad_and(p: &&Point) -> void: pass # error: syntax-error
fn bad_mut(p: mut &Point) -> void: pass # error: syntax-error
fn bad_fn(f: &fn() -> i32) -> void: pass # error: syntax-error
fn bad_power(p: **Point) -> void: pass # error: syntax-errorSee also: Modifiers, Optionality, And Grouping.
#Reference Expressions
A reference expression is a prefix operator and its operand. &e and &mut e make a reference, and *e reads through one.
fn demo(r: &mut Point, items: List[Point], i: i32) -> void:
inspect(&items[i])
edit := &mut r.x
copy := *r
*r = Point { x: 1, y: 2 }
made := &Point { x: 3, y: 4 }- ref.expr.make
&eand&mut eare prefix expressions. Their operand is aunary_expression. - ref.expr.literalA data expression is an ordinary operand, so
&Point { x: 1, y: 2 }and&mut Box::[i32] { value: 1 }are reference expressions around a literal. - ref.expr.deref-operand
*eis a prefix expression. Its operand is a postfix expression, another*e, or a prefix expression such as&xor-x. - ref.expr.deref.target
*emay be the left side of=and of a compound assignment, as in*r = vand*r += 1. - ref.expr.star-binary
*is a prefix operator only. Between two operands it stays multiplication, and*=stays compound assignment.
#Precedence
The prefix & and &mut bind like the prefix ! and the unary -. The prefix * binds like a postfix suffix, tighter than **.
- ref.prec.unary-ref
&and&mutareunary_expressionoperators. They bind more tightly than every binary operator and less tightly than every postfix suffix. - ref.prec.deref
*binds more tightly than every binary operator,**included, and less tightly than every postfix suffix. - ref.prec.postfixThe operand includes the postfix suffixes after it.
&a.b[i]is&(a.b[i]), and*r.xis*(r.x). - ref.prec.groupTo dereference before a suffix, group the operand:
(*r).x. - ref.prec.power-refThe operand of
&and&mutincludes**, as for unary-.&x ** 2is&(x ** 2). - ref.prec.power-derefThe operand of
**may be a dereference.*r ** 2is(*r) ** 2, and2 ** *ris2 ** (*r). - ref.prec.nestedPrefix operators nest from the right.
&*ris&(*r),*&xis*(&x), and-*ris-(*r). - ref.prec.operand-positionA
&or*that begins an operand is a prefix operator. After an operand it is the binary bitwise and or multiplication, soa & &bisa & (&b).
fn precedence(r: &mut Point, items: List[Point], i: i32, a: i32, b: i32) -> void:
inspect(&items[i].x) # inspect(&(items[i].x))
two := *r.x # *(r.x)
three := (*r).x # a field of the value
four := a & &b # bitwise and of a with a reference
five := a * *b # multiplication of a by a dereference
six := *r ** 2 # (*r) ** 2
seven := &r ** 2 # &(r ** 2)Why. A reader who knows
-a.band!a.balready knows&a.b. A dereference reads a value, so*r ** 2squares the value*r, asr.x ** 2squares a field.
#Operator Tokens
- ref.token.and-and
&&is the logical and operator and nothing else.&&xis a syntax error, so a reference to a reference is written& &xor&(&x). Error:syntax-error. - ref.token.caret
^is only the exclusive-or operator. It has no prefix form and no meaning in a type. Error:syntax-error. - ref.token.and-boundIn a generic bound list,
&still joins bounds, as inT < Display & Debug.
fn tokens(a: bool, b: bool, x: i32, y: i32) -> void:
both := a && b
bits := x & y
flip := x ^ yfn bad_expr(x: i32) -> void:
r := &&x # error: syntax-error
s := ^x # error: syntax-errorSee also: Unary And Binary Operators.
#Places And New Objects
&e and &mut e refer to e when e is a place, and to a new object holding a copy of e otherwise.
struct Point:
x: i32
y: i32
struct Holder:
point: *mut Point
fn make() -> Point:
Point { x: 1, y: 2 }
fn demo() -> Holder:
let mut p = Point { x: 0, y: 0 }
r := &mut p # refers to p, as a counted *mut Point
r.x = 5 # p.x is now 5
q := &mut p.y # refers to the field y of p
*q = 7 # p.y is now 7
n := &make() # a new object holding the result
m := &mut Point { x: 3, y: 4 } # a new object
Holder { point: &mut p } # a counted *mut Point too- ref.place.formsLocals and parameters of any binding form, and top-level bindings, are places for
&and&mut. A struct field of a place and a value reached through a reference are places too. - ref.place.through
*rwith a referenceris a place, and so is a field read throughr, such asr.x. The referenceris evaluated once, when the reference to the place is taken. - ref.place.elementA list element and a map value are places too. A reference to one is an element reference, which lives for one call.
- ref.expr.place
&eand&mut eon a place refer to that place. A later write to the place, through any path, is seen through the reference. - ref.expr.allocate
&eand&mut eon any other expression evaluateeand refer to a new object that holds the value. Each evaluation makes a new object. - ref.expr.allocate.writableA new object is writable, so
&mut T { ... }and&mut make()are valid. - ref.expr.kind.localWhere a local reference type is expected,
&eis a local reference&T, and&mut ea&mut T.Tis the type ofe. - ref.expr.kind.local.whereA local reference type is expected for a
&Tor&mut Tparameter, for a&selfor&mut selfreceiver, and for a binding annotated&Tor&mut T. - ref.expr.kind.countedEverywhere else
&eis a counted reference*T, and&mut ea*mut T. That includes an expected counted type, an expected type variable, and no expected type at all. - ref.place.aliasTwo references to one place, or two copies of one reference, see each other's writes.
- ref.place.lifetimeA counted reference keeps the place it refers to alive. A counted reference to a local may outlive the block that declares the local, and may be returned.
- ref.place.receiverA call of a
&self,&mut self,*self, or*mut selfmethod takes a reference to its receiver as&and&mutwould, with the place rules of this section. Reference Receivers states the call's rules.
Why. A reference to a value that is not a place still needs storage to refer to, so it gets a new object, as Go's
&T{...}does.
#Writable Places Of References
&mut e on a place needs the place to be writable.
- ref.place.writable
&mut eon a place requires a writable place. Alet mutlocal, amutparameter,mut self, and a value reached through a writable reference,*mutor&mut, are writable. A struct field of a writable place is writable too. - ref.place.writable.top-levelA top-level binding is writable when it is bound with
let mut. - ref.place.writable.collection
&mutof a list or a map value in a writable place is valid. A change through the reference changes that place. - ref.place.writable.enum
&mutof an enum value in a writable place is valid: alet mutlocal, amutparameter, or a struct field of one.*r = vthrough&mut Ereplaces the whole value. A payload is no place (ref.match.copy), so&mutof a payload binding is an error by the rows below. - ref.place.shared-any
&eneeds no writable place: it may refer to a:=binding, a plainletbinding, or a value parameter.
&mut of a place that is not writable is an error, by what holds the place:
| Rule | The place is held by | Error |
|---|---|---|
| ref.place.not-writable.shortShort binding | a := or using binding |
non-reassignable-binding |
| ref.place.not-writable.paramParameter | a value parameter or self that is not mut |
non-reassignable-parameter-binding |
ref.place.not-writable.letPlain let |
a let binding without mut |
not-writable |
| ref.place.not-writable.shared-referentRead-only reference | the value directly referred to by a read-only reference, *T or &T |
not-writable |
- ref.place.not-writable.fixFor a plain
letbinding and a value parameter, the diagnostic's fix-it writesmutbefore the binding's name.
fn invalid(count: i32) -> void:
let total = 0
r := &mut total # error: not-writable
fixed := 1
s := &mut fixed # error: non-reassignable-binding
t := &mut count # error: non-reassignable-parameter-bindingSee also: Writable Places.
#Conversions
A writable reference converts to a read-only one, and a counted reference lends itself as a local one.
- ref.convert.weakenA writable reference converts implicitly to the read-only one of its kind:
*mut Tto*T, and&mut Tto&T. A*mut Talso converts to*T?where an optional is expected. The conversion makes no new reference: both refer to the same place. - ref.convert.lendA counted reference converts implicitly to a local one where a local reference is expected:
*Tto&T, and*mut Tto&mut Tor&T. Both refer to the same place. - ref.convert.no-upgradeA read-only reference,
*Tor&T, never converts to a writable one,*mut Tor&mut T. Using one where a writable reference is expected is an error. Error:mutable-upgrade. - ref.convert.no-countedA local reference never converts to a counted one, as
ref.local.escape.countedstates. - ref.convert.listed-onlyReference conversions are limited to this section and Trait Value References.
- ref.convert.readonly-covariantA readonly container reference permits
*mut Uto*Uweakening through covariant container arguments and tuple elements. - ref.convert.readonly-optionThe same weakening enters an optional payload beneath a readonly reference.
- ref.convert.readonly-invariantThe conversion preserves invariant arguments and the operands of mutable references retained in the target.
- ref.convert.readonly-representationThe conversion preserves representation and reference identity, without copying elements or inserting wrappers.
fn edit(r: &mut i32) -> void:
*r += 1
fn view(r: &i32) -> i32:
*r
fn invalid(r: *i32) -> *mut i32:
edit(r) # error: mutable-upgrade
r # error: mutable-upgrade#Local References
A local reference, &T or &mut T, is lent to a call: a function may read and write through it, and pass it on, but never keep it.
struct Point:
x: i32
y: i32
struct Holder:
point: *mut Point
fn nudge(p: &mut Point) -> void:
p.x += 1
fn bump(p: &mut Point) -> void:
q := p # a local binding of a local reference
nudge(q) # passed on to a &mut parameter
fn demo(h: Holder) -> i32:
let mut origin = Point { x: 0, y: 0 }
nudge(&mut origin) # a local reference argument
let r: &mut Point = &mut origin # local: the annotation expects &mut
bump(r)
nudge(h.point) # a *mut Point lends itself as a &mut Point
origin.x- ref.local.usesA local reference may be the value of a parameter, a local binding, a call argument, or a receiver.
- ref.local.argumentPassing a local reference to a
&Tor&mut Tparameter is valid, so a function may pass on what it was lent. - ref.local.holdsA type holds a local reference when it is a local reference type, or a tuple type or a type alias whose type holds one.
- ref.local.selfA
&selfor&mut selfreceiver is a local reference, so these rules apply toselfin such a method. A method that keeps or returnsself, such as a builder, takes*selfor*mut self(Reference Receivers). - ref.local.escape.countedA local reference never converts to a counted one. Using one where a counted reference is expected, as for a
*Tparameter or field, is an error. Error:local-reference-escapes.
A local reference escapes when it reaches one of these positions, and each is an error:
| Rule | A local reference | Example | Error |
|---|---|---|---|
| ref.local.escape.fieldField | is stored in a struct field or an enum payload field, or a field's declared type holds one | point: &Point |
local-reference-escapes |
| ref.local.escape.elementCollection element | is stored as an element of a list, a map, or a set, by a literal or a method such as push |
points.push(p) |
local-reference-escapes |
| ref.local.escape.top-levelTop-level binding | is bound by a top-level binding | let origin_ref: &Point = &origin |
local-reference-escapes |
| ref.local.escape.returnResult | is returned, or a function's or a function type's declared result type holds one | fn pick(p: &Point) -> &Point |
local-reference-escapes |
| ref.local.escape.captureClosure capture | is captured by a closure, a captured self included |
fn() -> i32: p.x |
local-reference-escapes |
| ref.local.escape.type-argumentType argument | is a type argument, written or inferred, an optional's included | List[&Point], &Point? |
local-reference-escapes |
- ref.local.escape.fixThe diagnostic's fix-it writes
*in place of&in the local reference type it reports: the position's own type, as inpoint: *Point, or the escaping value's declared type, as inp: *Point. - ref.local.escape.fix.inferredFor a binding whose type is inferred from a local reference, as
q := pwithp: &Point, the fix-it writes*in the declared type it was inferred from, sop: *Point. - ref.local.escape.messageThe diagnostic's message names the position the reference escapes to, a row of the table above or a counted reference, and says that writing
*makes the reference counted.
struct Point:
x: i32
struct Holder:
point: &Point # error: local-reference-escapes
origin := Point { x: 0 }
let origin_ref: &Point = &origin # error: local-reference-escapes
fn keep(points: &mut List[*Point], p: &Point) -> void:
points.push(p) # error: local-reference-escapes
fn pick(p: &Point) -> &Point: # error: local-reference-escapes
p
fn reader(p: &Point) -> fn() -> i32:
fn() -> i32: p.x # error: local-reference-escapes
fn first(points: List[&Point]) -> i32: # error: local-reference-escapes
0Why. Most references are lent for one call, as Swift's
inoutparameters are. Writing*marks the few that are kept, so a reader sees which references outlive a call. Both kinds are still counted, so the check changes nothing at run time.
See also: Captured References, Reference Receivers.
#Reading Through A Reference
Field access and method calls read through a reference automatically, and *r reads the whole value.
struct Vec2:
x: f64
y: f64
impl Vec2:
fn len2(self) -> f64:
self.x * self.x + self.y * self.y
fn demo(r: &Vec2) -> f64:
copy := *r # a copy of the whole value
r.x + r.len2() + copy.y- ref.read.autoA field read or a method call on a reference applies to the value it refers to:
r.xreads the fieldxof that value, andr.len2()callslen2on it. - ref.deref.read
*rreads the whole valuerrefers to. The result is a copy. - ref.deref.kinds
*rreads through a counted and a local reference alike. - ref.deref.not-reference
*eon a value that is not a reference is an error. Error:type-mismatch. - ref.read.typeA read through a writable reference,
*mut Tor&mut T, has typeT, and the value it reaches is a writable place. - ref.read.shallowA read through a read-only reference,
*Tor&T, keeps its declared type; the reference makes only the value it refers to read-only. - ref.read.index.declaredAn index on a reference to a list, map, or string reads an element of the referenced value.
With Team { name: string, lead: *mut User } and t: &Team:
| Expression | Result |
|---|---|
t.name = "x" |
Error: not-writable; the referenced value is read-only. |
t.lead.age = +1 |
Valid; lead retains *mut User. |
t.lead.birthday() |
Valid for a &mut self method. |
x := t.lead |
x has type *mut User. |
*t |
A copy of type Team, with its declared field types. |
struct User:
age: i32
struct Team:
name: string
lead: *mut User
fn copy(t: &Team) -> Team:
*t
fn dup[T](t: &T) -> T:
*t
fn edit(t: &Team) -> void:
x := t.lead
x.age = +1#Matching Through A Reference
A pattern that tests a value reads through a reference, as a field access does, so a recursive enum is matched through its * links.
enum Expr:
Num(n: i64)
Add(left: *Expr, right: *Expr)
fn eval(e: &Expr) -> i64:
match e:
.Num(n) => n
.Add(.Num(a), r) => a + eval(r)
.Add(l, r) => eval(l) + eval(r)- ref.match.readA variant, tuple, data, literal, or range pattern matched against a reference of either kind,
*T,*mut T,&T, or&mut T, matches the value the reference refers to. - ref.match.nestedThe rule applies at every depth: a nested pattern reads through a payload field, a tuple element, or a struct field of reference type.
- ref.match.bindingA binding or
_matched against a reference takes the reference itself, sowhole => whole is eistrue. - ref.match.copyA payload or field that a pattern binds below such a read is a copy of it, read at the time of the match. A payload is not a place.
- ref.match.declared-typeA binding copied by a pattern through a reference keeps its declared type.
enum Holder:
Empty
Cell(value: *mut i64)
fn reset(h: &Holder) -> void:
match h:
.Cell(value) =>
*value = 0
.Empty => passWhy. Payloads are not places, so a match never lends part of an enum. A binding is a copy, and a later write to the matched place does not change it; an enum's layout can then change between variants under no reference.
See also: Payload Bindings, Exhaustiveness.
#Writing Through A Reference
*r = v replaces the value r refers to, and a field assignment through a writable reference writes the referenced value's field.
struct Point:
x: i32
y: i32
fn reset(r: &mut Point) -> void:
*r = Point { x: 0, y: 0 }
r.x += 1- ref.deref.write
*r = vand*r op= vreplace the valuerrefers to.rmust be a writable reference,*mutor&mut. Error:not-writable. - ref.write.fieldAssigning a field through a reference, as in
r.x = v, writes the field of the referenced value. The reference must be a writable one,*mutor&mut. Error:not-writable. - ref.write.seenThe write is seen through every reference to the place, and through the place itself.
- ref.write.no-exclusivityA writable reference,
*mut Tor&mut T, is not exclusive: other references to the same place may exist and be used, and no check limits them. - ref.write.indexAn element store through a reference to a list or a map, as in
r[i] = vorr[i] op= v, stores into the referenced value. A change of that value through a method call, as inr.push(x), changes it in place as well. The reference must be a writable one,*mutor&mut. Error:not-writable.
struct Point:
x: i32
y: i32
fn invalid(r: &Point) -> void:
*r = Point { x: 0, y: 0 } # error: not-writable
r.x = 1 # error: not-writableWhy. A counted reference never dangles, so a write through another path cannot free what a reference reads; exclusivity would forbid only aliasing that is safe.
#Identity
r is s is true when the references r and s refer to the same place.
@derive(Eq)
struct Point:
x: i32
y: i32
fn demo() -> bool:
let mut p = Point { x: 1, y: 2 }
let mut q = Point { x: 1, y: 2 }
r := &mut p
s := &p
t := &q
r is s && !(s is t) && *s == *t- ref.identity.same-place
r is swith two references is true when both refer to the same place, and false otherwise. Two places that hold equal values are different places. - ref.identity.placesTwo references refer to the same place when they start at the same root and read the same struct fields from it. A root is one execution of a local's declaration, one call's parameter, a top-level binding, or one new object.
- ref.identity.copiesA copy of a reference, a reference converted to another kind or to read-only, and a reference passed to or returned from a function refer to the place the original refers to. So do
&*rand&mut *r. - ref.identity.mutability
iscompares references of either kind and either mutability, such as a&Twith a*mut T, since all may refer to one place. - ref.identity.incompatible
isbetween references to different types is an error. Error:incompatible-identity-operands. - ref.identity.non-reference
isbetween a reference and a value without identity, such as a struct, is an error. Error:identity-requires-references. - ref.identity.valuesA struct, tuple, or enum value has no identity, even when it holds references, so
ison one is an error. Error:identity-requires-references. - ref.identity.optionalAn optional reference,
*T?, holds one reference or none. The reference matched out of.Some(r)refers to the placerrefers to.
struct Point:
x: i32
fn same(a: &i32, b: &i64, p: &Point, v: Point) -> void:
one := a is b # error: incompatible-identity-operands
two := p is v # error: identity-requires-referencesWhy. A counted reference in hd never dangles, so code may keep one in a field or a list. Code that keeps references needs to ask whether two of them are the same object, as a graph walk or an observer list does;
isanswers without reading the values.
#Equality Of References
== between two references is an error, because is and *r == *s say which comparison is meant.
- ref.identity.no-equality
==and!=with two reference operands are an error. Error:reference-equality. - ref.identity.no-equality.fixThe diagnostic has two fix-its: one writes
r is s, or!(r is s)for!=, and compares places; the other writes*r == *s, or*r != *s, and compares values. - ref.identity.generic-eqA reference type implements
Eqwhen its value type does, andHashwhen its value type does. They compare and hash the values the references refer to, as Reference Equality states. So generic code and a derivedEqcompare through references.
fn same(a: &i32, b: &i32) -> bool:
a == b # error: reference-equalityWhy. Go's
==on pointers compares addresses, and Rust's compares contents. An author coming from either language would guess wrong half the time, so neither guess compiles.
See also: Derived Implementations, Identity.
#Element References
&xs[i] and &mut xs[i] refer to the slot i of the list xs, and &m[k] and &mut m[k] to the value of the key k in the map m. An element reference lives for one call: it is passed to the call, and no name keeps it.
struct Player:
name: string
score: i32
fn reward(player: &mut Player, points: i32) -> void:
player.score += points
fn rewarded(players: List[Player], best: usize) -> List[Player]:
let mut out = players
reward(&mut out[best], 10) # changes out[best] during the call
out[best].score += 1 # the element is a place without a reference too
out.push(Player { name: "new", score: 0 }) # the call's access has ended
out- ref.elem.place
&xs[i]and&mut xs[i], wherexsis a list, are element references: they refer to the slotiof the list. A struct field read from the element extends the place, as in&mut xs[i].pos, and an element of an inner list is a slot of that list, as in&mut grid[r][c]. - ref.elem.map
&m[k]and&mut m[k], wheremis a map, are element references too: they refer to the value of the keykinm. Each rule of this section holds for them, with the key in place of the index. - ref.elem.indexThe index is evaluated and checked once, when the reference is taken. An index that is not less than the list's length is a checked runtime panic. Panic:
index-out-of-bounds. - ref.elem.map.keyA key that the map does not hold is a checked runtime panic when the reference is taken, as a read of
m[k]is. Panic:index-out-of-bounds. - ref.elem.slotAn element reference names a slot, never the value in it. A read through it sees whatever value its slot holds, and a write replaces that value. After elements move between slots, as a sort or a swap moves them, it sees the element in its slot.
- ref.elem.writable
&mut xs[i]needsxsin a writable place, as an element store does (types.coll.writable.store). Its fix-it makes the binding writable. Error:not-writable. - ref.elem.writable.shared
&xs[i]needs no writable place, so it may refer into a:=list or a value parameter. - ref.elem.through-reference
xsmay be a reference to a list:&r[i]and&mut r[i]refer to the slotiof the listrrefers to. Through a read-only reference,*Tor&T,&mut r[i]is an error. Error:not-writable. - ref.elem.copyCopying a pinned list gives a list with elements of its own. A change to the copy never reaches a reference into the original, and a write through such a reference never reaches the copy.
- ref.elem.replaceAssigning a whole list to the pinned list's place, as in
xs = [], is allowed. The old references keep the old elements and no longer refer intoxs. - ref.elem.identityTwo element references refer to the same place when they refer to the same slot of the same list's elements and read the same struct fields from it.
fn bump(score: &mut i32) -> void:
*score += 1
fn invalid() -> void:
xs := [+1, 2]
bump(&mut xs[0]) # error: not-writableWhy. A list grows by moving its elements. Go keeps the old array alive, so a write through an old pointer silently stops reaching the slice. A reference that lives for one call never sees the list move. Code that keeps an element between calls keeps its index. A handle that survives a sort is a reference stored in the list,
List[*mut Player], or an ID.
#Call-Scoped Access
An element reference is an argument of one call, and the collection's length holds still until that call returns.
fn bump(score: &mut i32) -> void:
*score += 1
fn record(scores: &mut List[i32], score: &mut i32) -> void:
scores.push(*score)
fn demo() -> void:
let mut scores = [+1, 2]
bump(&mut scores[0]) # valid: the access ends with the call
record(&mut scores, &mut scores[0]) # error: conflicting-access
best := &mut scores[1] # error: element-reference-outlives-call- ref.elem.callAn element reference may be written only as an argument of a function or method call, or as the receiver of a method call.
- ref.elem.accessThe reference's access to its collection begins when the reference is taken and ends when the call returns.
- ref.elem.keptAny other use of an element reference is an error: binding it to a name, assigning it, storing it in a field, an element, or a payload, and returning it. Error:
element-reference-outlives-call. - ref.elem.kept.fixThe diagnostic's fix-it keeps the index instead: each use of the reference names the element again, as
xs[i]. - ref.elem.access.lengthWhile an access lasts, nothing may change the collection's length. Reads of the collection, and writes to its elements, the referenced one included, stay valid.
- ref.elem.unaliasedA collection is unaliased when it is a local binding or a value parameter, or a struct field or element reached from one through no reference, and no escaping closure captures that binding.
- ref.elem.escapingA closure is escaping when it is passed as an argument, returned, stored in a field, an element, a payload, or a top-level binding, or captured by an escaping closure.
- ref.elem.conflictDuring an access to an unaliased collection, another argument or the receiver of the same call that may change the collection's length is an error. Error:
conflicting-access. - ref.elem.access.pinEvery access pins its collection while it lasts, so a length change that no rule rejects at compile time panics.
- ref.elem.pinnedWhile a list is pinned, an operation that changes its length is a checked runtime panic:
push,insert,remove_at, and apopor aclearthat removes an element. Panic:collection-pinned. - ref.elem.pinned.mapWhile a map is pinned, a store of a key it does not hold and a
removeof a key it holds are checked runtime panics. Panic:collection-pinned. - ref.elem.pinned.allowedReplacing an element keeps the length, so it is allowed while the list is pinned: an element store, a write through an element reference, and a sort or a swap written with element stores.
- ref.elem.pinned.endA collection that several accesses pin stays pinned until each has ended.
- ref.elem.callee-copyA callee may keep a copy of a counted reference parameter after it returns, as any counted reference may outlive its block (
ref.place.lifetime). A kept copy of an element reference pins its collection while the copy lives. - ref.elem.callee-copy.declaredA reference parameter is one whose declared type is a reference,
*T,*mut T,&T, or&mut T. A parameter declared as a type parameterTis not one, even whenTis a reference type: an element reference passed for it is a value the callee may store, so passing it is keeping it (ref.elem.kept). Error:element-reference-outlives-call.
An argument may change the collection's length when it does one of the following to the collection, or to a place that holds it:
| Rule | The argument or receiver | Example |
|---|---|---|
| ref.elem.conflict.referenceReference | takes &mut of it |
record(&mut scores, &mut scores[0]) |
| ref.elem.conflict.methodMethod call | calls a &mut self method on it |
place(&mut scores[0], scores.remove_at(1)) |
Note. An implementation may omit a pin where it can prove that no reference reaches the collection and that no callee keeps the element reference.
Why. This is Swift's Law of Exclusivity, narrowed to length. A conflict is caught at compile time where every path to the collection is in view, and by the pin where a reference or a global may reach it. Writes to other elements cannot move the referenced slot, so they stay legal.
See also: Element Reference Loops, Collection Values, Map Values.
#Captured References
A closure that captures a counted reference copies the reference, so the closure and its scope refer to the same place:
struct Point:
x: i32
y: i32
fn bump(p: *mut Point) -> fn() -> void:
fn() -> void: p.x = p.x + 1
fn demo() -> i32:
let mut origin = Point { x: 0, y: 0 }
step := bump(&mut origin)
step()
step()
origin.x- ref.capture.copyCapturing a counted reference copies the reference. The copy and the original refer to the same place, as any two copies of a reference do (
ref.place.alias). - ref.capture.write-throughA write through a captured
*mut Tchanges the place it refers to, and every other path to that place sees the write. - ref.capture.variableA captured
letvariable that holds a counted reference is shared like any variable. Assigning it another reference changes the place that the closure and its scope both use.
Why. A reference already names a place, so copying it shares that place.
- ref.capture.localA closure never captures a local reference, as
ref.local.escape.capturestates.
See also: Copied And Shared Captures,
ref.read.shallow.
#Not Yet Checked
- ref.check.not-yet-tuple-and-indexTwo forms are errors until later jobs define them. One is a reference to a tuple element, as in
&pair._0. The other is a reference to an index result that is neither a list element nor a map value, such as a slice&xs[1..3]or a userIndexcall. Error:references-not-yet-checked. - ref.check.formatter
hd fmtformats a file that writes references. It keeps each reference token as written,&mutand*mutincluded, and a second run changes nothing.
fn element(items: List[i32], pair: (i32, i32)) -> void:
part := &items[0..1] # error: references-not-yet-checked
left := &pair._0 # error: references-not-yet-checked