Expressions
In C, expressions and statements are different. Expressions have values. Statements don’t.
In Rust, if
and match
can produce values. Most of the control flow tools in C are statements. In Rust, they are all expressions.
Declarations
You may occasionally see code that seems to redeclare an existing variable, like this:
The let
declaration creates a new, second variable, of a different type. The type of the first variable line
is Result<String, io::Error>
. The second line
is a String
. Its definition supersedes the first’s for the rest of the block. This is called shadowing and is very common in Rust programs.
When an fn
is declared inside a block, its scope is the entire block—that is, it can be used throughout the enclosing block. A nested fn
cannot access local variables or arguments that happen to be in scope.
if
and match
if
and match
All blocks of an if
expression must produce values of the same type. Similarly, all arms of a match
expression must have the same type.
if let
if let
The given expr
either matches the pattern
, in which case block1
runs, or doesn’t match, and block2
runs.
Loops
The ..
operator produces a range, a simple struct with two fields: start
and end
. 0..20
is the same as std::ops::Range { start: 0, end: 20 }
. Ranges can be used with for
loops because Range
is an iterable type: it implements the std::iter::IntoIterator
trait.
Control Flow in Loops
Within the body of a loop
, you can give break
an expression, whose value becomes that of the loop:
A loop can be labeled with a lifetime. In the following example, 'search:
is a label for the outer for
loop. Thus, break 'search
exits that loop, not the inner loop:
A break
can have both a label and a value expression. Labels can also be used with continue
.
return
Expressions
return
Expressionsreturn
without a value is shorthand for return ()
.
We used the ?
operator to check for errors after calling a function that can fail:
Expressions that don’t finish normally are assigned the special type !
, and they’re exempt from the rules about types having to match. You can see !
in the function signature of std::process::exit()
:
The !
means that exit()
never returns. It’s a divergent function.
You can write divergent functions of your own using the same syntax, and this is perfectly natural in some cases:
Rust then considers it an error if the function can return normally.
Function and Method Calls
.
operator is ease with the types.
Syntax for generic types:
The symbol ::<...>
is affectionately known in the Rust community as the turbofish.
Fields and Elements
Expressions like these three are called lvalues, because they can appear on the left side of an assignment.
Extracting a slice from an array or vector is straightforward:
The ..=
operator produces end-inclusive (or closed) ranges, which do include the end value:
Arithmetic, Bitwise, Comparison, and Logical Operators
Rust uses !
instead of ~
for bitwise NOT.
Bit shifting is always sign-extending on signed integer types and zero-extending on unsigned integer types. Since Rust has unsigned integers, it does not need an unsigned shift operator, like Java’s >>>
operator.
Bitwise operations have higher precedence than comparisons, unlike C, so if you write x & BIT != 0
, that means (x & BIT) != 0
, as you probably intended. This is much more useful than C’s interpretation, x & (BIT != 0)
, which tests the wrong bit!
Type Casts
Rust does not have C’s increment and decrement operators ++
and --
.
Numbers may be cast from any of the built-in numeric types to any other.
Values of type bool
or char
, or of a C-like enum
type, may be cast to any integer type.
Some casts involving unsafe pointer types are also allowed.
Values of type
&String
auto-convert to type&str
without a cast.Values of type
&Vec<i32>
auto-convert to&[i32]
.Values of type
&Box<Chessboard>
auto-convert to&Chessboard
.
These are called deref coercions, because they apply to types that implement the Deref
built-in trait. The purpose of Deref
coercion is to make smart pointer types, like Box
, behave as much like the underlying value as possible. Using a Box<Chessboard>
is mostly just like using a plain Chessboard
, thanks to Deref
.
Closures
Rust has closures, lightweight function-like values. A closure usually consists of an argument list, given between vertical bars, followed by an expression:
Last updated