Operator expressions
OperatorExpression ->
BorrowExpression
| DereferenceExpression
| TryPropagationExpression
| NegationExpression
| ArithmeticOrLogicalExpression
| ComparisonExpression
| LazyBooleanExpression
| TypeCastExpression
| AssignmentExpression
| CompoundAssignmentExpression
Operators are defined for built in types by the Rust language.
Many of the following operators can also be overloaded using traits in std::ops or std::cmp.
Overflow
Integer operators will panic when they overflow when compiled in debug mode. The -C debug-assertions and -C overflow-checks compiler flags can be used to control this more directly. The following things are considered to be overflow:
- When
+,*or binary-create a value greater than the maximum value, or less than the minimum value that can be stored.
- Applying unary
-to the most negative value of any signed integer type, unless the operand is a literal expression (or a literal expression standing alone inside one or more grouped expressions).
- Using
/or%, where the left-hand argument is the smallest integer of a signed integer type and the right-hand argument is-1. These checks occur even when-C overflow-checksis disabled, for legacy reasons.
- Using
<<or>>where the right-hand argument is greater than or equal to the number of bits in the type of the left-hand argument, or is negative.
Note
The exception for literal expressions behind unary
-means that forms such as-128_i8orlet j: i8 = -(128)never cause a panic and have the expected value of -128.In these cases, the literal expression already has the most negative value for its type (for example,
128_i8has the value -128) because integer literals are truncated to their type per the description in Integer literal expressions.Negation of these most negative values leaves the value unchanged due to two's complement overflow conventions.
In
rustc, these most negative expressions are also ignored by theoverflowing_literalslint check.
Borrow operators
BorrowExpression ->
(`&`|`&&`) Expression
| (`&`|`&&`) `mut` Expression
| (`&`|`&&`) `raw` `const` Expression
| (`&`|`&&`) `raw` `mut` Expression
The & (shared borrow) and &mut (mutable borrow) operators are unary prefix operators.
When applied to a place expression, this expressions produces a reference (pointer) to the location that the value refers to.
The memory location is also placed into a borrowed state for the duration of the reference. For a shared borrow (&), this implies that the place may not be mutated, but it may be read or shared again. For a mutable borrow (&mut), the place may not be accessed in any way until the borrow expires.
&mut evaluates its operand in a mutable place expression context.
If the & or &mut operators are applied to a value expression, then a temporary value is created.
These operators cannot be overloaded.
let mut array = ;
Even though && is a single token (the lazy 'and' operator), when used in the context of borrow expressions it works as two borrows:
// same meanings:
let a = && 10;
let a = & & 10;
// same meanings:
let a = &&&& mut 10;
let a = && && mut 10;
let a = & & & & mut 10;
Raw borrow operators
&raw const and &raw mut are the raw borrow operators.
The operand expression of these operators is evaluated in place expression context.
&raw const expr then creates a const raw pointer of type *const T to the given place, and &raw mut expr creates a mutable raw pointer of type *mut T.
The raw borrow operators must be used instead of a borrow operator whenever the place expression could evaluate to a place that is not properly aligned or does not store a valid value as determined by its type, or whenever creating a reference would introduce incorrect aliasing assumptions. In those situations, using a borrow operator would cause undefined behavior by creating an invalid reference, but a raw pointer may still be constructed.
The following is an example of creating a raw pointer to an unaligned place through a packed struct:
let packed = Packed ;
// `&packed.f2` would create an unaligned reference, and thus be undefined behavior!
let raw_f2 = &raw const packed.f2;
assert_eq!;
The following is an example of creating a raw pointer to a place that does not contain a valid value:
use MaybeUninit;
let mut uninit = uninit;
// `&uninit.as_mut().field` would create a reference to an uninitialized `bool`,
// and thus be undefined behavior!
let f1_ptr = unsafe ;
unsafe
let init = unsafe ;
The dereference operator
DereferenceExpression -> `*` Expression
The * (dereference) operator is also a unary prefix operator.
When applied to a pointer or Box, it denotes the pointed-to location.
If the expression is of type &mut T, *mut T, or Box<T>, and is either a local variable, a (nested) field of a local variable or is a mutable place expression, then the resulting memory location can be assigned to.
When applied to a Box, the resultant place may be moved from.
Dereferencing a raw pointer requires unsafe.
On non-pointer types *x is equivalent to *std::ops::Deref::deref(&x) in an immutable place expression context and *std::ops::DerefMut::deref_mut(&mut x) in a mutable place expression context, except that when *x undergoes temporary lifetime extension, the dereferenced expression x also has its temporary scope extended.
# ;
let a = &7;
assert_eq!;
let b = &mut 9;
*b = 11;
assert_eq!;
let c = Boxnew;
let d: NoCopy = *c;
// The temporary holding the result of `String::new()` is extended
// to live to the end of the block, so `x` may be used in subsequent
// statements.
let x = &*Stringnew;
# x;
// The temporary holding the result of `String::new()` is dropped at
// the end of the statement, so it's an error to use `y` after.
let y = &*deref; // ERROR
# y;
The try propagation expression
TryPropagationExpression -> Expression `?`
The try propagation expression uses the value of the inner expression and the Try trait to decide whether to produce a value, and if so, what value to produce, or whether to return a value to the caller, and if so, what value to return.
Example
# use ParseIntError; let res = try_to_parse; println!; # assert!assert_eq!; assert_eq!;use ControlFlow; # #
Note
The
Trytrait is currently unstable, and thus cannot be implemented for user types.The try propagation expression is currently roughly equivalent to:
# #
Note
The try propagation operator is sometimes called the question mark operator, the
?operator, or the try operator.
The try propagation operator can be applied to expressions with the type of:
- [
Result<T, E>]Result::Ok(val)evaluates toval.Result::Err(e)returnsResult::Err(From::from(e)).
- [
Option<T>]Option::Some(val)evaluates toval.Option::NonereturnsOption::None.
- [
ControlFlow<B, C>][core::ops::ControlFlow]ControlFlow::Continue(c)evaluates toc.ControlFlow::Break(b)returnsControlFlow::Break(b).
- [
Poll<Result<T, E>>][core::task::Poll]Poll::Ready(Ok(val))evaluates toPoll::Ready(val).Poll::Ready(Err(e))returnsPoll::Ready(Err(From::from(e))).Poll::Pendingevaluates toPoll::Pending.
- [
Poll<Option<Result<T, E>>>][core::task::Poll]Poll::Ready(Some(Ok(val)))evaluates toPoll::Ready(Some(val)).Poll::Ready(Some(Err(e)))returnsPoll::Ready(Some(Err(From::from(e)))).Poll::Ready(None)evaluates toPoll::Ready(None).Poll::Pendingevaluates toPoll::Pending.
Negation operators
NegationExpression ->
`-` Expression
| `!` Expression
These are the last two unary operators.
This table summarizes the behavior of them on primitive types and which traits are used to overload these operators for other types. Remember that signed integers are always represented using two's complement. The operands of all of these operators are evaluated in value expression context so are moved or copied.
| Symbol | Integer | bool |
Floating Point | Overloading Trait |
|---|---|---|---|---|
- |
Negation* | Negation | std::ops::Neg |
|
! |
Bitwise NOT | Logical NOT | std::ops::Not |
* Only for signed integer types.
Here are some example of these operators
let x = 6;
assert_eq!;
assert_eq!;
assert_eq!;
Arithmetic and logical binary operators
ArithmeticOrLogicalExpression ->
Expression `+` Expression
| Expression `-` Expression
| Expression `*` Expression
| Expression `/` Expression
| Expression `%` Expression
| Expression `&` Expression
| Expression `|` Expression
| Expression `^` Expression
| Expression `<<` Expression
| Expression `>>` Expression
Binary operators expressions are all written with infix notation.
This table summarizes the behavior of arithmetic and logical binary operators on primitive types and which traits are used to overload these operators for other types. Remember that signed integers are always represented using two's complement. The operands of all of these operators are evaluated in value expression context so are moved or copied.
| Symbol | Integer | bool |
Floating Point | Overloading Trait | Overloading Compound Assignment Trait |
|---|---|---|---|---|---|
+ |
Addition | Addition | std::ops::Add |
std::ops::AddAssign |
|
- |
Subtraction | Subtraction | std::ops::Sub |
std::ops::SubAssign |
|
* |
Multiplication | Multiplication | std::ops::Mul |
std::ops::MulAssign |
|
/ |
Division*† | Division | std::ops::Div |
std::ops::DivAssign |
|
% |
Remainder**† | Remainder | std::ops::Rem |
std::ops::RemAssign |
|
& |
Bitwise AND | Logical AND | std::ops::BitAnd |
std::ops::BitAndAssign |
|
| |
Bitwise OR | Logical OR | std::ops::BitOr |
std::ops::BitOrAssign |
|
^ |
Bitwise XOR | Logical XOR | std::ops::BitXor |
std::ops::BitXorAssign |
|
<< |
Left Shift | std::ops::Shl |
std::ops::ShlAssign |
||
>> |
Right Shift*** | std::ops::Shr |
std::ops::ShrAssign |
* Integer division rounds towards zero.
** Rust uses a remainder defined with truncating division. Given remainder = dividend % divisor, the remainder will have the same sign as the dividend.
*** Arithmetic right shift on signed integer types, logical right shift on unsigned integer types.
† For integer types, division by zero panics.
Here are examples of these operators being used.
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
Comparison operators
ComparisonExpression ->
Expression `==` Expression
| Expression `!=` Expression
| Expression `>` Expression
| Expression `<` Expression
| Expression `>=` Expression
| Expression `<=` Expression
Comparison operators are also defined both for primitive types and many types in the standard library.
Parentheses are required when chaining comparison operators. For example, the expression a == b == c is invalid and may be written as (a == b) == c.
Unlike arithmetic and logical operators, the traits for overloading these operators are used more generally to show how a type may be compared and will likely be assumed to define actual comparisons by functions that use these traits as bounds. Many functions and macros in the standard library can then use that assumption (although not to ensure safety).
Unlike the arithmetic and logical operators above, these operators implicitly take shared borrows of their operands, evaluating them in place expression context:
# let a = 1;
# let b = 1;
a == b;
// is equivalent to
eq;
This means that the operands don't have to be moved out of.
| Symbol | Meaning | Overloading method |
|---|---|---|
== |
Equal | std::cmp::PartialEq::eq |
!= |
Not equal | std::cmp::PartialEq::ne |
> |
Greater than | std::cmp::PartialOrd::gt |
< |
Less than | std::cmp::PartialOrd::lt |
>= |
Greater than or equal to | std::cmp::PartialOrd::ge |
<= |
Less than or equal to | std::cmp::PartialOrd::le |
Here are examples of the comparison operators being used.
assert!;
assert!;
assert!;
assert!;
assert!;
assert!;
Lazy boolean operators
LazyBooleanExpression ->
Expression `||` Expression
| Expression `&&` Expression
The operators || and && may be applied to operands of boolean type. The || operator denotes logical 'or', and the && operator denotes logical 'and'.
They differ from | and & in that the right-hand operand is only evaluated when the left-hand operand does not already determine the result of the expression. That is, || only evaluates its right-hand operand when the left-hand operand evaluates to false, and && only when it evaluates to true.
let x = false || true; // true
let y = false && panic!; // false, doesn't evaluate `panic!()`
Type cast expressions
TypeCastExpression -> Expression `as` TypeNoBounds
A type cast expression is denoted with the binary operator as.
Executing an as expression casts the value on the left-hand side to the type on the right-hand side.
An example of an as expression:
#
#
as can be used to explicitly perform coercions, as well as the following additional casts. Any cast that does not fit either a coercion rule or an entry in the table is a compiler error. Here *T means either *const T or *mut T. m stands for optional mut in reference types and mut or const in pointer types.
Type of e |
U |
Cast performed by e as U |
|---|---|---|
| Integer or Float type | Integer or Float type | Numeric cast |
| Enumeration | Integer type | Enum cast |
bool or char |
Integer type | Primitive to integer cast |
u8 |
char |
u8 to char cast |
*T |
*V (when compatible) |
Pointer to pointer cast |
*T where T: Sized |
Integer type | Pointer to address cast |
| Integer type | *V where V: Sized |
Address to pointer cast |
&m₁ [T; n] |
*m₂ T 1 |
Array to pointer cast |
*m₁ [T; n] |
*m₂ T 1 |
Array to pointer cast |
| Function item | Function pointer | Function item to function pointer cast |
| Function item | *V where V: Sized |
Function item to pointer cast |
| Function item | Integer | Function item to address cast |
| Function pointer | *V where V: Sized |
Function pointer to pointer cast |
| Function pointer | Integer | Function pointer to address cast |
| Closure 2 | Function pointer | Closure to function pointer cast |
Semantics
Numeric cast
-
Casting between two integers of the same size (e.g. i32 -> u32) is a no-op (Rust uses 2's complement for negative values of fixed integers)
assert_eq!; assert_eq!; assert_eq!; assert_eq!;
-
Casting from a larger integer to a smaller integer (e.g. u32 -> u8) will truncate
assert_eq!; assert_eq!; assert_eq!; assert_eq!; assert_eq!; assert_eq!;
-
Casting from a smaller integer to a larger integer (e.g. u8 -> u32) will
- zero-extend if the source is unsigned
- sign-extend if the source is signed
assert_eq!; assert_eq!; assert_eq!; assert_eq!; assert_eq!;
-
Casting from a float to an integer will round the float towards zero
NaNwill return0- Values larger than the maximum integer value, including
INFINITY, will saturate to the maximum value of the integer type. - Values smaller than the minimum integer value, including
NEG_INFINITY, will saturate to the minimum value of the integer type.
assert_eq!; assert_eq!; assert_eq!; assert_eq!; assert_eq!; assert_eq!;
-
Casting from an integer to float will produce the closest possible float *
- if necessary, rounding is according to
roundTiesToEvenmode *** - on overflow, infinity (of the same sign as the input) is produced
- note: with the current set of numeric types, overflow can only happen on
u128 as f32for values greater or equal tof32::MAX + (0.5 ULP)
assert_eq!; assert_eq!; assert_eq!; - if necessary, rounding is according to
-
Casting from an f32 to an f64 is perfect and lossless
assert_eq!; assert_eq!; assert!;
-
Casting from an f64 to an f32 will produce the closest possible f32 **
- if necessary, rounding is according to
roundTiesToEvenmode *** - on overflow, infinity (of the same sign as the input) is produced
assert_eq!; assert_eq!; assert_eq!; assert!; - if necessary, rounding is according to
* if integer-to-float casts with this rounding mode and overflow behavior are not supported natively by the hardware, these casts will likely be slower than expected.
** if f64-to-f32 casts with this rounding mode and overflow behavior are not supported natively by the hardware, these casts will likely be slower than expected.
*** as defined in IEEE 754-2008 §4.3.1: pick the nearest floating point number, preferring the one with an even least significant digit if exactly halfway between two floating point numbers.
Enum cast
Casts an enum to its discriminant, then uses a numeric cast if needed. Casting is limited to the following kinds of enumerations:
- Unit-only enums
- Field-less enums without explicit discriminants, or where only unit-variants have explicit discriminants
assert_eq!;
assert_eq!;
assert_eq!;
Casting is not allowed if the enum implements [Drop].
Primitive to integer cast
falsecasts to0,truecasts to1charcasts to the value of the code point, then uses a numeric cast if needed.
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!;
u8 to char cast
Casts to the char with the corresponding code point.
assert_eq!;
assert_eq!;
Pointer to address cast
Casting from a raw pointer to an integer produces the machine address of the referenced memory. If the integer type is smaller than the pointer type, the address may be truncated; using usize avoids this.
Address to pointer cast
Casting from an integer to a raw pointer interprets the integer as a memory address and produces a pointer referencing that memory.
Warning
This interacts with the Rust memory model, which is still under development. A pointer obtained from this cast may suffer additional restrictions even if it is bitwise equal to a valid pointer. Dereferencing such a pointer may be undefined behavior if aliasing rules are not followed.
A trivial example of sound address arithmetic:
let mut values: = ;
let p1: *mut i32 = values.as_mut_ptr;
let first_address = p1 as usize;
let second_address = first_address + 4; // 4 == size_of::<i32>()
let p2 = second_address as *mut i32;
unsafe
assert_eq!;
Pointer-to-pointer cast
*const T / *mut T can be cast to *const U / *mut U with the following behavior:
-
If
TandUare both sized, the pointer is returned unchanged.Example
let x: i32 = 42; let p1: *const i32 = &x; let p2: *const u8 = p1 as *const u8; // The pointer address remains the same. assert_eq!;
-
If
Tis unsized andUis sized, the cast discards all metadata that completes the wide pointerTand produces a thin pointerUconsisting of the data part of the unsized pointer.Example
let slice: & = &; let ptr: *const = slice as *const ; // Cast from wide pointer (*const [i32]) to thin pointer (*const i32) // discarding the length metadata. let data_ptr: *const i32 = ptr as *const i32; assert_eq!;
- If
TandUare both unsized, the pointer is also returned unchanged. In particular, the metadata is preserved exactly. The cast can only be performed if the metadata is compatible according to the below rules:
-
When
TandUare unsized with slice metadata, they are always compatible. The metadata of a slice is the number of elements, so casting*[u16] -> *[u8]is legal but will result in reducing the number of bytes by half.Example
let slice: & = &; let ptr: *const = slice as *const ; let byte_ptr: *const = ptr as *const ; assert_eq!;
- When
TandUare unsized with trait object metadata, the metadata is compatible only when all of the following holds:-
The principal trait must be the same.
Example
let x: i32 = 42; let ptr_foo: *const dyn Foo = &x as *const dyn Foo; // You can't cast to a different principal trait. let ptr_bar: *const dyn Bar = ptr_foo as *const dyn Bar; // ERROR -
Auto traits may be removed.
Example
; unsafe let s = S; let ptr_send: *const = &s; // Removing an auto trait. let ptr_no_send: *const dyn Foo = ptr_send as *const dyn Foo; -
Auto traits may be added only if they are a super trait of the principal trait.
Example
; unsafe let s = S; let ptr_no_send: *const dyn Foo = &s; // Adding an auto trait. let ptr_send: *const = ptr_no_send as *const ;# ; # # unsafe # # let s = S; # let ptr_no_send: *const dyn Foo = &s; // Same as above, except trait Foo does not have Send as a super trait. let ptr_send: *const = ptr_no_send as *const ; // ERROR -
Trailing lifetimes may only be shortened.
Example
*const*const -
Generics (including lifetimes) and associated types must match exactly.
Example
let x = ; let ptr_i32: *const dyn = &x; // You can't cast to a different generic parameter. let ptr_u32: *const dyn = ptr_i32 as *const dyn ; // ERROR*const dyn where 'a: 'b, 'b: 'a, A: HasType, B: , // Forces equality
-
-
When
TorUis a struct or tuple type whose last field is unsized, it has the same metadata and compatibility rules as its last field.Example
; let slice: & = &; let ptr: *const = slice; // The metadata (length 3) is preserved when casting to a struct // where the last field is the unsized type `[u8]`. let wrapper_ptr: *const Wrapper = ptr as *const Wrapper; // And preserved when casting back. let ptr_back: *const = wrapper_ptr as *const ; assert_eq!;
Assignment expressions
AssignmentExpression -> Expression `=` Expression
An assignment expression moves a value into a specified place.
An assignment expression consists of a mutable assignee expression, the assignee operand, followed by an equals sign (=) and a value expression, the assigned value operand.
In its most basic form, an assignee expression is a place expression, and we discuss this case first.
The more general case of destructuring assignment is discussed below, but this case always decomposes into sequential assignments to place expressions, which may be considered the more fundamental case.
Basic assignments
Evaluating assignment expressions begins by evaluating its operands. The assigned value operand is evaluated first, followed by the assignee expression.
For destructuring assignment, subexpressions of the assignee expression are evaluated left-to-right.
Note
This is different than other expressions in that the right operand is evaluated before the left one.
It then has the effect of first dropping the value at the assigned place, unless the place is an uninitialized local variable or an uninitialized field of a local variable.
Next it either copies or moves the assigned value to the assigned place.
An assignment expression always produces the unit value.
Example:
let mut x = 0;
let y = 0;
x = y;
Destructuring assignments
Destructuring assignment is a counterpart to destructuring pattern matches for variable declaration, permitting assignment to complex values, such as tuples or structs. For instance, we may swap two mutable variables:
let = ;
// Swap `a` and `b` using destructuring assignment.
= ;
In contrast to destructuring declarations using let, patterns may not appear on the left-hand side of an assignment due to syntactic ambiguities. Instead, a group of expressions that correspond to patterns are designated to be assignee expressions, and permitted on the left-hand side of an assignment. Assignee expressions are then desugared to pattern matches followed by sequential assignment.
The desugared patterns must be irrefutable: in particular, this means that only slice patterns whose length is known at compile-time, and the trivial slice [..], are permitted for destructuring assignment.
The desugaring method is straightforward, and is illustrated best by example.
#
# let = ;
= ;
= ;
Struct = Struct ;
// desugars to:
Identifiers are not forbidden from being used multiple times in a single assignee expression.
Underscore expressions and empty range expressions may be used to ignore certain values, without binding them.
Note that default binding modes do not apply for the desugared expression.
Note
The desugaring restricts the temporary scope of the assigned value operand (the RHS) of a destructuring assignment.
In a basic assignment, the temporary is dropped at the end of the enclosing temporary scope. Below, that's the statement. Therefore, the assignment and use is allowed.
# let x; ; // OKConversely, in a destructuring assignment, the temporary is dropped at the end of the
letstatement in the desugaring. As that happens before we try to assign tox, below, it fails.# # # let x; = ; // ERRORThis desugars to:
# # # let x;
Note
Due to the desugaring, the assigned value operand (the RHS) of a destructuring assignment is an extending expression within a newly-introduced block.
Below, because the temporary scope is extended to the end of this introduced block, the assignment is allowed.
# # let x; = ; // OKThis desugars to:
# # let x; // OKHowever, if we try to use
x, even within the same statement, we'll get an error because the temporary is dropped at the end of this introduced block.# # let x; ; // ERRORThis desugars to:
# # let x; ;
Compound assignment expressions
CompoundAssignmentExpression ->
Expression `+=` Expression
| Expression `-=` Expression
| Expression `*=` Expression
| Expression `/=` Expression
| Expression `%=` Expression
| Expression `&=` Expression
| Expression `|=` Expression
| Expression `^=` Expression
| Expression `<<=` Expression
| Expression `>>=` Expression
Compound assignment expressions combine arithmetic and logical binary operators with assignment expressions.
For example:
let mut x = 5;
x += 1;
assert!;
The syntax of compound assignment is a mutable place expression, the assigned operand, then one of the operators followed by an = as a single token (no whitespace), and then a value expression, the modifying operand.
Unlike other place operands, the assigned place operand must be a place expression.
Attempting to use a value expression is a compiler error rather than promoting it to a temporary.
Evaluation of compound assignment expressions depends on the types of the operands.
If the types of both operands are known, prior to monomorphization, to be primitive, the right hand side is evaluated first, the left hand side is evaluated next, and the place given by the evaluation of the left hand side is mutated by applying the operator to the values of both sides.
# use ;
#
Note
This is unusual. Elsewhere left to right evaluation is the norm.
See the eval order test for more examples.
Otherwise, this expression is syntactic sugar for using the corresponding trait for the operator (see expr.arith-logic.behavior) and calling its method with the left hand side as the receiver and the right hand side as the next argument.
For example, the following two statements are equivalent:
# use AddAssign;
Note
Surprisingly, desugaring this further to a fully qualified method call is not equivalent, as there is special borrow checker behavior when the mutable reference to the first operand is taken via autoref.
# use AddAssign;# use AddAssign;# use AddAssign;
As with normal assignment expressions, compound assignment expressions always produce the unit value.
Warning
Avoid writing code that depends on the evaluation order of operands in compound assignments as it can be unusual and surprising.