r[expr]
r[expr.syntax]
Expression -> ExpressionWithoutBlock | ExpressionWithBlock ExpressionWithoutBlock -> OuterAttribute* ExpressionWithoutBlockNoAttrs ExpressionWithoutBlockNoAttrs -> LiteralExpression | PathExpression | OperatorExpression | GroupedExpression | ArrayExpression | AwaitExpression | IndexExpression | TupleExpression | TupleIndexingExpression | StructExpression | CallExpression | MethodCallExpression | FieldExpression | ClosureExpression | AsyncBlockExpression | ContinueExpression | BreakExpression | RangeExpression | ReturnExpression | UnderscoreExpression | MacroInvocation ExpressionWithBlock -> OuterAttribute* ExpressionWithBlockNoAttrs ExpressionWithBlockNoAttrs -> BlockExpression | ConstBlockExpression | UnsafeBlockExpression | LoopExpression | IfExpression | MatchExpression
r[expr.intro] An expression may have two roles: it always produces a value, and it may have effects (otherwise known as “side effects”).
r[expr.evaluation] An expression evaluates to a value, and has effects during evaluation.
r[expr.operands] Many expressions contain sub-expressions, called the operands of the expression.
r[expr.behavior] The meaning of each kind of expression dictates several things:
r[expr.structure] In this way, the structure of expressions dictates the structure of execution. Blocks are just another kind of expression, so blocks, statements, expressions, and blocks again can recursively nest inside each other to an arbitrary depth.
[!NOTE] We give names to the operands of expressions so that we may discuss them, but these names are not stable and may be changed.
r[expr.precedence]
The precedence of Rust operators and expressions is ordered as follows, going from strong to weak. Binary Operators at the same precedence level are grouped in the order given by their associativity.
| Operator/Expression | Associativity |
|---|---|
| [Paths][expr.path] | |
| [Method calls][expr.method] | |
| [Field expressions][expr.field] | left to right |
| [Function calls][expr.call], [array indexing][expr.array.index] | |
[?][expr.try] | |
Unary [-][expr.negate] [!][expr.negate] [*][expr.deref] [borrow][expr.operator.borrow] | |
[as][expr.as] | left to right |
[*][expr.arith-logic] [/][expr.arith-logic] [%][expr.arith-logic] | left to right |
[+][expr.arith-logic] [-][expr.arith-logic] | left to right |
[<<][expr.arith-logic] [>>][expr.arith-logic] | left to right |
[&][expr.arith-logic] | left to right |
[^][expr.arith-logic] | left to right |
| [|][expr.arith-logic] | left to right |
[==][expr.cmp] [!=][expr.cmp] [<][expr.cmp] [>][expr.cmp] [<=][expr.cmp] [>=][expr.cmp] | Require parentheses |
[&&][expr.bool-logic] | left to right |
| [||][expr.bool-logic] | left to right |
[..][expr.range] [..=][expr.range] | Require parentheses |
[=][expr.assign] [+=][expr.compound-assign] [-=][expr.compound-assign] [*=][expr.compound-assign] [/=][expr.compound-assign] [%=][expr.compound-assign] [ &=][expr.compound-assign] [|=][expr.compound-assign] [^=][expr.compound-assign] [<<=][expr.compound-assign] [>>=][expr.compound-assign] | right to left |
[return][expr.return] [break][expr.loop.break] [closures][expr.closure] |
r[expr.operand-order]
r[expr.operand-order.default] The following list of expressions all evaluate their operands the same way, as described after the list. Other expressions either don't take operands or evaluate them conditionally as described on their respective pages.
r[expr.operand-order.operands-before-primary] The operands of these expressions are evaluated prior to applying the effects of the expression. Expressions taking multiple operands are evaluated left to right as written in the source code.
[!NOTE] Which subexpressions are the operands of an expression is determined by expression precedence as per the previous section.
For example, the two next method calls will always be called in the same order:
# // Using vec instead of array to avoid references # // since there is no stable owned array iterator # // at the time this example was written. let mut one_two = vec![1, 2].into_iter(); assert_eq!( (1, 2), (one_two.next().unwrap(), one_two.next().unwrap()) );
[!NOTE] Since this is applied recursively, these expressions are also evaluated from innermost to outermost, ignoring siblings until there are no inner subexpressions.
r[expr.place-value]
r[expr.place-value.intro] Expressions are divided into two main categories: place expressions and value expressions; there is also a third, minor category of expressions called assignee expressions. Within each expression, operands may likewise occur in either place context or value context. The evaluation of an expression depends on both its own category and the context it occurs within.
r[expr.place-value.place-memory-location] A place expression is an expression that represents a memory location.
r[expr.place-value.place-expr-kinds] These expressions are paths that refer to variables or static variables, dereferences (*expr), array indexing expressions (expr[expr]), field references (expr.f), tuple indexing expressions (expr.0), and parenthesized place expressions.
r[expr.place-value.value-expr-kinds] All other expressions are value expressions.
r[expr.place-value.value-result] A value expression is an expression that represents an actual value.
r[expr.place-value.place-context] The following contexts are place expression contexts:
if let, match, or while let expression.[!NOTE] Historically, place expressions were called lvalues and value expressions were called rvalues.
r[expr.place-value.assignee] An assignee expression is an expression that appears in the left operand of an assignment expression. Explicitly, the assignee expressions are:
r[expr.place-value.parenthesis] Arbitrary parenthesization is permitted inside assignee expressions.
r[expr.move]
r[expr.move.intro] When a place expression is evaluated in a value expression context, or is bound by value in a pattern, it denotes the value held in that memory location.
r[expr.move.copy] If the type of that value implements Copy, then the value will be copied.
r[expr.move.requires-sized] In the remaining situations, if that type is Sized, then it may be possible to move the value.
[!NOTE] A
&mut Tcoerced to a type known to be a reference or raw pointer type is reborrowed rather than moved. See [coerce.reborrow].
r[expr.move.movable-place] Only the following place expressions may be moved out of:
Fields of a struct, union, tuple, or enum variant, whether accessed by a field or tuple indexing expression or matched by a pattern, of a place expression that can be moved out of and whose type does not implement Drop.
struct D; impl Drop for D { fn drop(&mut self) {} } struct S(D); let x = S(D); let y = x.0; // OK: `S` does not implement `Drop`, though `D` does.
struct S(String); impl Drop for S { fn drop(&mut self) {} } let x = S(String::new()); let y = x.0; // ERROR: Cannot move a field out of a type that implements `Drop`.
Elements or subarrays of an array that can be moved out of, when bound by value in a slice pattern.
let x = [String::new(), String::new()]; let [a, _] = x; // OK: Moves the first element out of `x`.
let x = [String::new()]; let a = x[0]; // ERROR: An index expression cannot be moved out of.
Parenthesized expressions whose enclosed operand can be moved out of.
The result of dereferencing an expression of type Box<T> that can itself be moved out of.
[!NOTE] A place expression cannot be moved out of while it is borrowed. See [expr.operator.borrow.lifetime].
r[expr.move.deinitialization] After moving out of a place expression that evaluates to a variable, the location is deinitialized and cannot be read from again until it is reinitialized.
r[expr.move.place-invalid] In all other cases, trying to use a place expression in a value expression context is an error, unless the expression is reborrowed (see [coerce.reborrow]).
r[expr.mut]
r[expr.mut.intro] The following contexts are mutable place expression contexts:
&mut) or a mutable raw borrow expression (&raw mut).ref mut binding whose ref mut is written in the pattern.The mutable place expressions, the place expressions that may be used in these contexts, are listed in [expr.mut.valid-places]. All other place expressions are immutable place expressions.
[!NOTE] A
ref mutbinding mode that arises from matching a reference with a non-reference pattern does not put the scrutinee in a mutable place expression context, so it does not cause an index or dereference within the scrutinee to useIndexMutorDerefMut. This behavior is settled: rust-lang/rust#44848, which tracked it, was closed with the current handling judged adequate and stabilized. The place that the binding borrows must nonetheless be a mutable place expression (see [expr.mut.other-borrows]).# let mut a = (0u8,); let mut v: Vec<Option<&mut (u8,)>> = vec![Some(&mut a)]; if let Some((x,)) = v[0] { *x = 1; } // ERROR: `v[0]` uses `Index`, so the field that `x` borrows is not mutable.# let mut a = (0u8,); let mut v: Vec<Option<&mut (u8,)>> = vec![Some(&mut a)]; if let Some(&mut (ref mut x,)) = v[0] { *x = 1; } // OK: The written `ref mut` makes `v[0]` use `IndexMut`.
r[expr.mut.operands] When a place expression is in a mutable place expression context, so is each place expression it is built on, after automatic dereferencing: the enclosed operand of a parenthesized expression, the container operand of a field or tuple indexing expression, the indexed operand of an index expression, the operand of a dereference, whether written or automatic, and the scrutinee that a pattern destructures. In such a context, an index expression that is not built in (see [expr.array.index.array]) uses IndexMut when its operand's type implements IndexMut for the index type of the Index implementation it uses, and Index otherwise; a dereference, written or automatic, of a type other than a reference, raw pointer, or Box<T> (see [expr.deref.traits]) uses DerefMut when that type implements it, and Deref otherwise. The implicit mutable borrow that IndexMut and DerefMut take is a mutable place expression context for their operand.
# use std::rc::Rc; # let mut a = 0u8; struct P { p: *mut u8 } let rc = Rc::new(P { p: &mut a }); unsafe { *rc.p = 1; } // OK: `Rc` does not implement `DerefMut`, so `*rc` uses `Deref`, and `*rc.p` is mutable because `rc.p` is a `*mut u8`.
# let mut a = 0u8; let mut v: Vec<&mut u8> = vec![&mut a]; let r = &v; *v[0] = 1; // ERROR: `v[0]` uses `IndexMut`, which mutably borrows `v` while `r` borrows it. let _ = r;
r[expr.mut.valid-places] Only the following place expressions are mutable place expressions. Most entries make a place expression mutable exactly when the operand it is built on is; the dereference of a *mut T is mutable whatever its operand, and so, under the conditions of its entry, is the dereference of a &mut T.
Mutable variables.
Variables not yet initialized on any control flow path reaching the context that initializes them (see [variable.init]).
Fields of a struct, union, tuple, or enum variant, whether accessed by a field or tuple indexing expression or matched by a pattern: mutable when the place expression that holds the struct, union, tuple, or enum, after automatic dereferencing (see [expr.field.autoref-deref]), is.
Parenthesized expressions: mutable when the enclosed operand is.
Indexing of an array or slice by a value of type usize, or indexing that uses IndexMut (see [expr.mut.operands]): mutable when the indexed operand, after automatic dereferencing (see [expr.array.index.autoderef]), is.
Elements or subslices of an array or slice matched by a slice pattern: mutable when the array or slice is.
Dereferences of a *mut T pointer.
Dereferences of a Box<T>: mutable when the operand is.
Dereferences that use DerefMut (see [expr.mut.operands]): mutable when the operand is.
Dereferences of a place expression of type &mut T, where that place expression is a mutable place expression, is a variable, or is obtained from one of those by any sequence of the following operations:
&mut U or Box<U>.usize or by a slice pattern, in either case possibly after automatic dereferencing where that dereferencing is of an expression of type &mut U or Box<U>.struct S<'a>(&'a mut u8); fn f(x: &mut u8, q: &mut &mut u8, s: S<'_>, b: Box<&mut u8>, c: Box<S<'_>>, a: [&mut u8; 1], o: Option<&mut S<'_>>) { *x = 1; // OK: `x` is a variable, though not declared `mut`. **q = 1; // OK: `*q` is a dereference of a `&mut` held by a variable. *s.0 = 1; // OK: `s.0` is a field of a variable. **b = 1; // OK: `*b` is a dereference of a `Box` held by a variable. *c.0 = 1; // OK: `c.0` is a field of the dereference of a `Box` held by a variable. *a[0] = 1; // OK: `a[0]` is an element of an array held by a variable. if let Some(S(y)) = o { **y = 1; } // OK: The `ref mut` binding mode borrows a field reached through a `&mut` held by a variable. }
fn f(r: &&mut u8) { **r = 1; // ERROR: `*r` is reached through a shared reference. }
fn f(v: Vec<&mut u8>) { *v[0] = 1; // ERROR: `v[0]` indexes through `IndexMut`, which mutably borrows `v`, so `v` must be mutable. }
[!NOTE] The
muton a variable governs assignment to and mutable borrows of the variable and the places it owns, such as its fields and the contents of aBoxit holds. A mutable reference is the only way to access its referent (see [type.pointer.reference.mut.copy]), so a&mut Tcan be written through whether or not the variable holding it is declaredmut. The contents of aBox<T>take the mutability of the place that holds the box, but a&mut Uamong those contents is still the only way to access its own referent. Indexing or dereferencing throughIndexMutorDerefMutmutably borrows the operand (see [expr.mut.operands]), so the operand must itself be mutable even when the place reached through it is the referent of a&mut. Indexing an array or slice by ausizeand dereferencing a&mutreference or aBoxtake no such borrow, so they may lie on the path from a variable to a&mut Twithout anymutbeing required.
r[expr.mut.invalid-places] Using any other place expression in one of the contexts listed in [expr.mut.intro] is an error.
[!NOTE] In particular, the dereference of a shared reference or of a
*const Tpointer is never a mutable place expression, and neither is a field, element, or dereference reached through one unless a dereference of a*mut Tpointer intervenes.
r[expr.mut.other-borrows] The place that a ref mut binding mode borrows (see [patterns.ident.binding]), and the place *e that a coercion of a &mut T to a *mut U borrows (see [coerce.reborrow]), must also be mutable place expressions. Neither borrow is a mutable place expression context, so neither causes an index or dereference within the place to use IndexMut or DerefMut (see [expr.mut.operands]).
fn f(r: &mut &mut (u8,)) { let (x,) = *r; // OK: The `ref mut` binding mode borrows `(**r).0`, which is mutable. *x = 1; }
fn f(r: &&mut (u8,)) { let (x,) = *r; // ERROR: The `ref mut` binding mode borrows `(**r).0`, which is reached through a shared reference. *x = 1; }
fn f(r: &mut &mut u8) -> *mut u8 { let p: *mut u8 = *r; // OK: The coercion borrows `**r`, which is mutable. p }
fn f(r: &&mut u8) -> *mut u8 { let p: *mut u8 = *r; // ERROR: The coercion borrows `**r`, which is reached through a shared reference. p }
[!NOTE] A mutable place expression also cannot be mutated while it is borrowed. See [expr.operator.borrow.lifetime].
r[expr.temporary]
When using a value expression in most place expression contexts, a temporary unnamed memory location is created and initialized to that value. The expression evaluates to that location instead, except if promoted to a static. The drop scope of the temporary is usually the end of the enclosing statement.
r[expr.super-macros]
r[expr.super-macros.intro] Certain built-in macros may create temporaries whose scopes may be extended. These temporaries are super temporaries and these macros are super macros. Invocations of these macros are super macro call expressions. Arguments to these macros may be super operands.
[!NOTE] When a super macro call expression is an extending expression, its super operands are extending expressions and the scopes of the super temporaries are extended. See [destructors.scope.lifetime-extension.exprs].
r[expr.super-macros.format_args]
format_args!r[expr.super-macros.format_args.super-operands] Except for the format string argument, all arguments passed to format_args! are super operands.
# fn temp() -> String { String::from("") } // Due to the call being an extending expression and the argument // being a super operand, the inner block is an extending expression, // so the scope of the temporary created in its trailing expression // is extended. let _ = format_args!("{}", { &temp() }); // OK
r[expr.super-macros.format_args.super-temporaries] The super operands of format_args! are implicitly borrowed and are therefore place expression contexts. When a value expression is passed as an argument, it creates a super temporary.
# fn temp() -> String { String::from("") } let x = format_args!("{}", temp()); x; // <-- The temporary is extended, allowing use here.
The expansion of a call to format_args! sometimes creates other internal super temporaries.
let x = { // This call creates an internal temporary. let x = format_args!("{:?}", 0); x // <-- The temporary is extended, allowing its use here. }; // <-- The temporary is dropped here. x; // ERROR
// This call doesn't create an internal temporary. let x = { let x = format_args!("{}", 0); x }; x; // OK
[!NOTE] The details of when
format_args!does or does not create internal temporaries are currently unspecified.
r[expr.super-macros.pin]
pin!r[expr.super-macros.pin.super-operands] The argument to pin! is a super operand.
# use core::pin::pin; # fn temp() {} // As above for `format_args!`. let _ = pin!({ &temp() }); // OK
r[expr.super-macros.pin.super-temporaries] The argument to pin! is a value expression context and creates a super temporary.
# use core::pin::pin; # fn temp() {} // The argument is evaluated into a super temporary. let x = pin!(temp()); // The temporary is extended, allowing its use here. x; // OK
r[expr.implicit-borrow]
r[expr.implicit-borrow-intro] Certain expressions will treat an expression as a place expression by implicitly borrowing it. For example, it is possible to compare two unsized slices for equality directly, because the == operator implicitly borrows its operands:
# let c = [1, 2, 3]; # let d = vec![1, 2, 3]; let a: &[i32]; let b: &[i32]; # a = &c; # b = &d; // ... *a == *b; // Equivalent form: ::std::cmp::PartialEq::eq(&*a, &*b);
r[expr.implicit-borrow.application] Implicit borrows may be taken in the following expressions:
*).format_args! except the format string.&mut T reborrowed by a coercion to a reference type (see [coerce.reborrow]).r[expr.overload]
Many of the following operators and expressions can also be overloaded for other types using traits in std::ops or std::cmp. These traits also exist in core::ops and core::cmp with the same names.
r[expr.attr]
r[expr.attr.restriction] Outer attributes before an expression are allowed only in a few specific cases:
r[expr.attr.never-before] They are never allowed before: