Control flow¶
Candela has if, three loops, and match. Every body is a brace-delimited
block, and braces are never optional.
if / else¶
fn main() {
let temperature = 30;
if temperature > 25 {
print("warm");
} else if temperature > 10 {
print("mild");
} else {
print("cold");
}
}
The condition is not parenthesised. It is a bool expression: candela has no
truthiness, so compare explicitly rather than testing a number or a string. A
condition of another type is a compile error naming the type it has, and a
condition typed any is checked when it runs and raises bad_downcast on
anything but a bool; see errors. The same holds for
else if, for while, and for the expression form below.
A condition that tests a value with is gives its body more than a bool: a
tested variable has the type it was tested for, and a variant test such as
opt is Some(x) binds the payload. See
testing a type.
if as an expression¶
An if written where a value is expected produces a value. Each branch is a
single expression with no semicolon, and an else branch is required so that
every path yields something. A match works the same
way.
while¶
while repeats a block for as long as its condition holds. The condition is a
bool, exactly as in an if, and a value of another type is rejected the same
way it is there.
for¶
for walks a range or a collection.
A range is written start..end and covers start up to but not including
end. Leaving the start out begins at zero.
Iterating a list binds each element in turn; iterating a map binds each key. Iterating a string binds each character as a one-character string.
fn main() {
for word in ["a", "b"] {
print(word);
}
let counts = {"x": 1, "y": 2};
for key in counts {
print(key, counts[key]);
}
}
The loop variable belongs to the loop and is not in scope after it.
loop¶
loop repeats until something breaks out of it.
break and continue¶
break leaves the innermost loop; continue skips to its next iteration. Both
work in for, while, and loop.
match¶
match compares a value against a list of arms and runs the first that fits.
An arm is pattern => { ... } with a block, or pattern => expression, with
one expression and a comma before the next arm; the comma after the last arm is
optional. _ is the catch-all. A match needs at least one arm that is not the
wildcard, and the wildcard comes last.
fn main() {
let code = 2;
match code {
1 => print("one"),
2 => {
print("two");
print("and more");
}
_ => print("something else"),
}
}
Arms match by equality, so any type you can compare works, strings included.
| between patterns makes one arm for several values.
fn main() {
match "b" {
"a" | "b" => print("early"),
"c" => print("third"),
_ => print("other"),
}
}
Matching an enum matches on the variant instead, and binds the payload; see Enums.
match as an expression¶
A match written where a value is expected produces a value: the value of the
arm that fits. Like an if used as a value, it has to give one on every path,
so it needs a _ arm unless it matches an enum and lists every variant. A
braced arm holds one expression, as the branches of an if used as a value do.
fn main() {
let n = 3;
let size = match n {
0 => "none",
1 | 2 => "few",
_ => "many",
};
print(size);
}
Blocks and scope¶
A bare { ... } is a block. It groups statements and scopes the variables
declared inside it, which is occasionally useful for keeping a temporary out of
the surrounding function.
Loop bodies, if branches, and match arms are blocks and scope the same way.
See Variables for the scoping rules.
A block holds statements. A fn declaration written inside one is a compile
error, since functions belong at the top level of the file. See
Functions.