A boolean value is either 'true' or 'false'. Booleans appear naturally wherever you make a decision: in 'if' conditions, loop filters, and comparisons. Loft adds a third state called 'null' — meaning "no value" — which behaves like false whenever a boolean is expected.
This design means you rarely need to write a separate null-check: '!x' is true both when x is false and when x is null.
fn main() {
A comparison produces a boolean result directly. '!' flips a boolean: true → false, false → true.
assert(!(3 > 2 + 4), "3 is not greater than 6");
assert(true as text == "true", "Convert boolean to text");
Logical operators: and / or
'and' (also '&&') is true only when both sides are true. 'or' (also '||') is true when at least one side is true. Both use short-circuit evaluation: the right side is only evaluated if the left side does not already determine the result. This matters when the right side could produce null or has a side effect.
assert(1 > 0 and 2 > 1, "Both conditions true");
assert(1 > 2 or 3 > 2, "Second condition true");
assert(!(1 > 2 && 3 > 2), "First false → whole 'and' is false");
assert(!(1 > 2 || 3 > 4), "Both false → 'or' is false");
Null in boolean context
Division by zero (and other failed operations) produce null. Null in a boolean context is treated as false, so '!' is true. This lets you write guard clauses without a separate null-check syntax: if !result { ... handle missing value ... }
zero = 0;
assert(!(12 / zero), "null from division-by-zero is false-like");
assert(12 > 0, "positive integer is true");
Bitwise operators
While 'and'/'or' work on true/false values, bitwise operators work on the individual bits of an integer. They are useful for flags, masks, and low-level data manipulation.
'&' — keeps only bits set in BOTH operands (AND) '|' — keeps bits set in EITHER operand (OR) '<<' — shift bits left N positions (×2^N) '>>' — shift bits right N positions (÷2^N)
Tip: '&' binds less tightly than comparison operators. Use parentheses when you mix bitwise and comparison expressions in the same condition.
assert((0x0f & 0xa8) == 8, "Bitwise AND: keeps only bits in both");
assert((0xf0 | 0x0f) == 0xff, "Bitwise OR: combines bits from either");
assert(1 << 4 == 16, "Left shift: 1 × 2^4 = 16");
assert(256 >> 3 == 32, "Right shift: 256 ÷ 2^3 = 32");
Negation
'!' is the logical NOT operator. It flips any boolean expression.
assert(!false, "not false is true");
assert(!(1 > 2), "not false comparison is true");
Formatting booleans
Booleans can be embedded in a format string like any other value. The '^' alignment specifier centres the value in a field of given width. '<' aligns left, '>' aligns right (right-alignment is the default).
assert("1{true:^7}2" == "1 true 2", "Centred boolean in field of width 7");
assert("{false}" == "false", "Plain boolean format");
Common pitfall: '&' vs 'and'
'&' is bitwise AND on integers; 'and' (or '&&') is logical AND on booleans. Writing 'a & b' when you mean 'a and b' usually compiles but gives wrong results because it operates on the numeric representation of the booleans. Always use 'and' / '&&' for boolean logic.
flag_a = 3 > 1;
true
flag_b = 4 > 2;
true
assert(flag_a and flag_b, "Correct: logical AND on two booleans");
}