if decides which code block to perform based on the truth of one or more conditional expressions. Consider:
fn abs(x i32) i32: mut result i32; if x < 0: result = -x else: result = x result
if is used by this function to correctly return the positive, absolute value of x. x < 0 is a conditional expression that evaluates to true whenever x is a negative number. When the conditional evaluates as true, the subsequent block is performed, which converts x to its positive equivalent. When the conditional is not true, the block after the else is performed instead, which captures the value of x. The else clause is optional; it need not be specified if there is no alternative action to perform when the condition is false.
Before covering the logic of conditional expressions, let's summarize various ways to use if.
if as expression
As with blocks, if can be used as an expression. This (along with implicit return) provides us with another way to write this function:
fn abs(x i32) i32: if x < 0: -x else: x
If we have more than one condition to test, each corresponding to a different block to perform, every condition after the first is preceded with elif (short for "else if"). For example:
fn abs(x i32) i32: if x < 0: -x elif x == 0: 0 else: x
Conditional expressions are often just simple comparisons, as shown above. However, complex conditional expressions can be formulated that use logical operators, pattern matching, functions or methods. Let's walk through the possibilities, beginning with the basics.
True vs. False
A conditional expression evaluates to true or false. The integer number 0, a Null value, and false all evaluate to false. All other values evaluate to true. Thus:
// This statement will never execute a += 1 if 0 // since 0 evaluates to false // This statement will always execute if "hello": a += 1
This applies to the (first) value returned by a function or method call:
if a.isOdd: a += 1
With most values, we can compare whether they are the same by using an equivalence operator. The == operator returns true if two values have the same content and type. != (not equal) is true if they do not. For example:
x == 3.0 // Returns true if the local variable x's value is 3.0 x+1 != y // Returns true if x is not exactly 1 greater than y true != false // Returns true
As with the arithmetic operators, the == and != operators are implemented as type-specific methods. Thus, each type can establish its own rules for equivalence. For example, two floating point numbers could be evaluated as equal if they are within some margin of error (resulting from floating point calculation rounding errors). Similarly, two lists might be equal if they have the exact same contents. A type must implement the == method to ensure its values can be compared.
Comparison operators determine whether one value is greater or less than some other value. They work only on types that are comparable, such as numbers.
x >= 2 // Returns true if x is greater than or equal to 2 (>, <, <= are variants) 2 <= 2.0 // Returns false any time the types do not agree
Sometimes a conditional expression requires checking multiple conditions and then combining those results together into a single true/false determination. The boolean operators 'and', 'or' and 'not' ('!') are used to accomplish this. These operators are distinctly different than the bitwise logic operators ('&', '|', and '~') introduced in an earlier section.
0==3 or not 2<3 and 3==3 // Returns true ('and' is evaluated before 'or')
The logical operators work mostly how you would expect:
- As mentioned earlier, they treat 0, Null and false as false. All other values are considered true.
- The boolean operators 'and' and 'or' will stop evaluating any time the answer is known for sure.
Thus, the .isEmpty method will never be performed in this expression:
false and list.isEmpty
- The logical operators have a lower precedence than evaluation operators, but higher than assignment. 'not' has the highest precedence, then 'and', then 'or'.
Conditional expressions need not only be found on control flow statements like if. They may also be incorporated as part of any arbitrary expression. For example:
imm isOne = (a == 1) // isOne will be either true or false