Variables and control flow
Inside a function body, statements run in order. jihoo has one way to declare a
variable, two branching forms (if and match), two loops
(while and for, with break and continue), and return.
fn collatz(n: i64) -> i64 { let steps = 0 while n != 1 { if n % 2 == 0 { n = n / 2 } else { n = 3 * n + 1 } steps = steps + 1 } return steps}let name = value declares a variable and gives it its first value; there are
no uninitialized variables. The type is inferred from the value, or written
out, in which case the value must have that type (and literals take it):
let n = 10 // i64let b: u8 = 200 // the literal is a u8let p = Point { x: 1, y: 2 }Variables can be assigned again (n = n + 1); there is no mut. Parameters
are variables too, except comptime parameters, which cannot be assigned.
A variable is visible from its let to the end of the block it is declared in.
Declaring a name that is already visible makes a new variable that hides the old
one from then on, even with a different type:
let x = "42"let x = 42 // a new variable; the str is no longer reachable by nameBlocks
Section titled “Blocks”A block { ... } is also an expression. Its statements run in a scope of
their own, and if the last one is an expression, that is the block’s value;
otherwise the block’s value is unit.
let area = { let w = right - left let h = bottom - top w * h}- Variables declared in the block end with it; a name it declares hides an outer one only inside the block.
return,breakandcontinueinside the block leave the function or loop around it, as anywhere else.- Where a block follows, a
{starts that block and not a block expression:if { c } { ... }is an error, as struct literals are in conditions. - An expression macro can make statements this way, returning
quote({ ... }).
Assignment
Section titled “Assignment”place = value stores into a place: a variable, a field (p.x), an array
element (a[i]), and in compiled code what a pointer points to (*p, p[i],
p.x through a pointer), or the contents of a cell (*c, c.x).
Places nest: line.a.x = 1, grid[r][c] = 0.
Since structs and arrays are values, assigning to a part of one changes only that variable.
Compound assignment
Section titled “Compound assignment”place op= value combines the place’s value with value and stores the result
back, for every arithmetic and bitwise operator:
i += 1 // i = i + 1total -= costgrid[r][c] *= 2flags |= 0x4 // also &=, ^=, <<=, >>=, /= and %=name += "!" // concatenation, on str- It works on the same places as
=, and follows the operator’s rules:+=on twoi32s, on floats, on strings, and on a pointer and ani64(p += 1);&=,|=and^=on bools too. - The value takes the place’s type, so
x += 1works for any integerx, and the result must have the place’s type again:x += ywithx: i32andy: i64is an error. - The place is evaluated once. In
a[next()] += 1,nextis called one time, and the element is read beforevalueis evaluated, as ina[i] = a[i] + value. - It is a statement, like
=, not an expression:let y = x += 1is an error. There is no++,&&=or||=.
if n < 0 { print("negative")} else if n == 0 { print("zero")} else { print("positive")}The condition must be a bool — there is no implicit conversion from integers
or pointers — and needs no parentheses; the braces are required. if is a
statement, not an expression; to choose a value, use a
match expression:
let sign = match n < 0 { true => -1, false => 1 }A struct literal cannot appear directly in a condition, since
if p == Point { ... } would be ambiguous; wrap it in parentheses:
if p == (Point { x: 0, y: 0 }) { ... }.
while cond { body } runs the body as long as the condition, a bool, holds:
let n = 1while n < limit { n = n * 2}for walks a range of integers, the elements of a list, an array or a
str (its bytes, as u8), or the items of a value whose type has an
Iterator. With two names, the first is the index, an
i64:
for i in 0..len(xs) { ... } // 0, 1, ... len(xs) - 1for x in xs { total += x }for i, x in xs { print(i) }for b in "hi" { print(b) } // 104, 105lo..hicounts up fromlotohi - 1, and is empty whenhi <= lo. Both are integers of the same type (a literal takes the other’s type, so0..nworks for any integern), and both are evaluated once, before the loop starts. The counter never steps pasthi, so a range may end at its type’s largest value.for x in xswalks the valuexshad when the loop started:xsis evaluated once, and the body changing it (push(xs, x),xs[i] = 0) does not change what the loop sees, since lists are values. Neither the VM nor native code copies the list for this unless the body does change it.for x in it, for a type with anIterator, callsnextonitand then on what each round’snextleft, until it givesNone.itis a value, so the loop leaves the variable as it was.- The names are new variables in each round, visible only in the body.
xis a copy of the element: assigning to it changes neither the list nor the next round. Assigning to the index does not change the next round either.
break and continue
Section titled “break and continue”break leaves the innermost loop at once, and continue skips the rest of
its body and goes on with the loop’s next round (the next test of a while
condition, the next element or number of a for):
let i = 0let sum = 0while true { i = i + 1 if i % 2 == 0 { continue } // skip even numbers if i > 9 { break } // done sum = sum + i}print(sum) // 1 + 3 + 5 + 7 + 9 = 25- Only the innermost loop is affected; there are no loop labels. To leave
several loops at once, put them in a function and
return. - They work anywhere inside the loop’s body: in
ifblocks, inmatchandselectarms, and in the code of a statement macro. - A closure body is a function of its own, so a
breakin a closure cannot leave a loop around the closure; it is an error, as is abreakorcontinueoutside any loop. - Code right after a
breakorcontinuein the same block never runs.
return
Section titled “return”return value leaves the function with a value; a function without a result
type ends with return or by reaching the end of its body. A function that has
a result must return on every path that reaches the end of its body. The
compiler does not reason about conditions, so a loop such as while true { ... }
still needs a return after it:
error: f.jh:3:1: missing `return`: `find` must return i64An exhaustive match whose arms all return is enough, since it
has no way to fall through.
Other statements
Section titled “Other statements”| statement | page |
|---|---|
an expression on its own, usually a call: f(x) |
Functions |
match value { pattern => ... } |
Pattern matching |
go f(x) and select { ... } |
Tasks and channels |
name!(...), a statement macro |
Macros |
There are no nested function declarations; use a named function at the top
level, or a closure (let f = fn(x: i64) -> i64 { ... }).