Bindings and statements.
A function body is a block of statements that run in order. This page covers let bindings, assignment, scopes and shadowing, return, expression statements, drop, and briefly let static and module-level const.
Syntax#
Block = '{' Statement* '}'
Statement = LetStatement | Assignment | ExpressionStatement | DropStatement
| ReturnStatement | IfStatement | WhileStatement | 'break' | 'continue'
| FailStatement | UnsafeBlock | StaticIf | StaticFor
LetStatement = 'let' ( 'mut' | 'static' )? Identifier ( ':' Type )? '=' Expression
| 'let' Pattern '=' Expression
Assignment = Expression '=' Expression
ExpressionStatement = Expression
DropStatement = 'drop' Expression
ReturnStatement = 'return' Expression?
UnsafeBlock = 'unsafe' Block
ConstDeclaration = 'pub'? 'const' Identifier ':' Type '=' ExpressionStatements need no separator. Line breaks and spaces both work:
let a = 1 let b = 2is two statements.returnwith no expression is allowed only directly before}. It returns().
let bindings#
let name = ebinds an immutable local. Its type is the type ofe.let name: T = echeckseagainstT. Literals inetake their type fromT. A mismatch is SEM0037.Every
letneeds an initializer.let x: i32alone is a parse error (PAR0001).let mut name = emakes the binding assignable.A binding may stay unused. Names starting with
_are ordinary names.let _ = eis rejected (SEM0087). Usedrop e.Binding a non-Copy local to a new name moves it and needs
move:let q = move p. Without it the compiler reports OWN0003. See Ownership and moves.
Pattern lets#
let Pattern = edestructurese. The pattern must be irrefutable for the type ofe(SEM0133 otherwise; useif letormatch).Struct patterns name fields:
let Pair { left, right: extra } = move pair...skips the rest.A non-Copy named variable as the initializer needs
move:let Pair { left, .. } = move pair. Without it the compiler reports OWN0003.A temporary, such as a call result or a literal, needs no
move:let Pair { left, .. } = make().There are no tuple patterns. Read tuple elements with
.0,.1, and so on.See Patterns and match for the full pattern grammar.
Assignment#
place = estoreseinto an existing place. Assignment is a statement, not an expression.A place is a
mutlocal, a field of a place (p.x), an indexed element (a[i]), or a dereference (r.*).Assigning to an immutable binding, or to a field or element of one, is SEM0035. Parameters are immutable unless declared
mut v: T.Assigning through a shared reference
&T, or to a non-place such as a call, is SEM0036.Writing through
&mut Tusesr.* = e. Field access through a reference needs no explicit.*:r.x = 5andr.*.x = 5both work.The assigned value must have the place's type (SEM0037).
There is no compound assignment.
x += 1is PAR0010: "Silk has no compound assignment; writex = x + 1instead ofx += 1".
Scopes and shadowing#
A binding is visible from the statement after its
letto the end of its block.let x = x + 1with no earlierxis SEM0006.There is no bare block statement. A
{at statement start does not open a scope. Blocks come only withif,while,matcharms,unsafe, functions and closures.Two
lets in the same block cannot share a name (SEM0008).A
letin a nested block may reuse an outer name. The inner binding shadows the outer one until the block ends. The outer binding is unchanged.A
letin the function body may reuse a parameter name and shadows it.
Expression statements#
An expression on its own is a statement only if its type is
()ornever(SEM0087 otherwise).To discard any other result, write
drop e. To keep it, bind it, return it, or pass it on.A call to an
effect fnproduces anEffectvalue. It does nothing until it isrun. See Effects and run.drop eevaluateseand ends ownership of the result.drop pconsumes the localpwithoutmove. Usingplater is OWN0001.A statement that starts with
(continues the previous line's expression as a call.let a = ffollowed by a line(3)meanslet a = f(3).A statement that starts with
[never continues the previous line. Indexing can't cross a line break.
Returns#
return eleaves the function withe. Its type must match the result type (SEM0129).A function or effect body with no result type gets an implicit
return ()at its end.A non-unit function must return on every reachable path (SEM0130). A call that returns
neveralso ends a path, as does awhile trueloop with nobreak.See Control flow for
if,while,breakandcontinue, and Typed failures forfail.
let static and const#
let static name = eevaluateseat compile time and binds the result.emay call onlystatic fnfunctions (SEM0176 otherwise).let static mutis not allowed. See Static evaluation.const NAME: T = edeclares a module-level constant. The type annotation is required:const A = 5is SEM0086.A constant's type is
bool,char, an integer or float type,string, a fixed array of these, or a struct or enum:const ORIGIN: Point = Point { x: 1, y: 1 }andconst STEPS: [i32; 3] = [1, 2, 3]are constants. Any other type, such as&[u8], is SEM0086: "Invalid constant: a constant cannot have type&'static [u8]; its type must bebool,char, an integer or float primitive,string, a fixed array of these, or a struct or enum".A struct with a
Dropimpl can't be a constant. Its initializer is SEM0176.&NAMEborrows a constant for'static, so it can be passed where a&'staticreference is expected. See Borrowing.A constant initializer may use operators and
static fncalls:const C: i32 = 1 + 2.pub constexports the constant.
Examples#
struct Point {
x: i32,
y: i32,
}
fn reset(target: &mut i32) {
target.* = 0
}
pub fn main() -> i32 {
let mut total = 1
total = total + 1
let mut point = Point { x: 1, y: 2 }
point.x = 10
let mut slots: [i32; 3] = [1, 2, 3]
let index: usize = 2
slots[index] = 30
let mut spare = 5
reset(&mut spare)
if total != 2 {
return 1
}
if point.x + point.y != 12 {
return 2
}
if slots[index] != 30 {
return 3
}
if spare != 0 {
return 4
}
return 0
}struct Pair {
left: i32,
right: i32,
}
fn make() -> Pair {
return Pair { left: 40, right: 2 }
}
pub fn main() -> i32 {
let value = 1
if value == 1 {
let value = 2
if value != 2 {
return 1
}
}
let Pair { left, right: extra } = make()
let Pair { left: first, .. } = make()
drop first
drop make()
return left + extra - 42 + value - 1
}struct Point {
x: i32,
y: i32,
}
const BASE: i32 = 40
const ORIGIN: Point = Point { x: 1, y: 1 }
const STEPS: [i32; 3] = [1, 2, 3]
static fn offset(step: i32) -> i32 {
return step * 2
}
fn first(steps: &'static [i32; 3]) -> i32 {
return steps[0]
}
pub fn main() -> i32 {
let static extra = offset(1)
if BASE + extra != 42 {
return 1
}
if ORIGIN.x + STEPS[2] != 4 || first(&STEPS) != 1 {
return 2
}
return 0
}Diagnostics#
| Code | Meaning |
|---|---|
| PAR0001 | Required token missing, e.g. let x: i32 with no initializer |
| PAR0002 | Unexpected tokens, e.g. ; as a terminator or let static mut |
| PAR0010 | A habit from another language, e.g. x += 1 |
| SEM0006 | Name with no visible binding |
| SEM0008 | A let repeats a name already bound in the same block |
| SEM0035 | Assignment through an immutable binding |
| SEM0036 | Assignment target is not a writable binding, field, index or &mut dereference |
| SEM0037 | Initializer or assigned value has the wrong type |
| SEM0086 | Constant without a type annotation, or type or initializer outside the constant rules |
| SEM0087 | Expression statement whose result is not () or never, including let _ = e |
| SEM0129 | return value does not match the result type |
| SEM0130 | A reachable path of a non-unit function falls through |
| SEM0133 | Refutable pattern in a let |
| SEM0176 | let static or const reaches work unavailable at compile time |
| OWN0001 | Use of a binding after it was moved or dropped |
| OWN0003 | A move that must be written with move |
See also#
Expressions and operators · Control flow · Patterns and match · Ownership and moves · Borrowing · Values and primitive types · Functions and methods · Static evaluation · Effects and run · Typed failures · Services and providers