Operators
Precedence
Tightest first. An operator binds its operands more tightly than anything below it in this table.
| Level | Operators | Groups |
|---|---|---|
| 15 | a[i] a? |
left |
| 14 | f(x) |
left |
| 13 | a.b a::b |
left |
| 12 | !a -a +a |
right |
| 11 | * / % |
left |
| 10 | + - |
left |
| 9 | is |
left |
| 8 | as |
left |
| 7 | < <= > >= |
left |
| 6 | == != |
left |
| 5 | && |
left |
| 4 | \|\| |
left |
| 2 | a..b |
none |
| 1 | = += -= *= /= %= |
right |
Parentheses group, and a grouped expression is whatever is inside it.
a? is error propagation, which passes a
failed Result or an empty Option on to the caller.
a..b is a range of the numbers from
a up to, and not including, b. Everything but assignment binds tighter, so
0..n + 1 is 0..(n + 1). Ranges don't chain: 0..5..10 is an error.
Important
as and is bind looser than arithmetic, which is not how they read. Both
take a type on the right rather than an expression, so everything the arithmetic
operators have already claimed is the left operand.
a + b as i64 is (a + b) as i64, not a + (b as i64).
use std;
let a: i32 = 7;
let b: i32 = 2;
let together = a + b as i64; // (a + b) as i64
let separate = a + (b as i64); // the other reading, written out
println($"{together} {separate}");
The two give the same result here, but they do not in general: the cast applies
to a different value, so a sum that overflows an i32 overflows before it
widens. Write the parentheses when the difference matters.
Member access binds tighter than unary, so -point.x negates the field rather
than the value. A call binds tighter than the access it is part of, which is why
value.doubled() reads as one thing.
Arithmetic
| Operator | On | Produces |
|---|---|---|
* / % - |
two numbers | their coercion |
+ |
two numbers | their coercion |
+ |
two strings | string |
+ |
a string and a char |
string |
use std;
println($"{7 * 6}");
println($"{7 / 2}");
println($"{7 % 2}");
println("Hello, " + "world");
println("x" + 'y');
+ joins text only for those two cases. To put a number or a bool into a
string, interpolate it.
let text = "count: " + 3;
Comparison
<, <=, > and >= compare two numbers and produce a bool. A char takes
part as its code point.
use std;
println($"{1 < 2}");
println($"{'a' < 'b'}");
== and != work on the numeric types, string, bool and char, the types
that have a value to compare.
use std;
let name = "Ada";
println($"{1 == 1}");
println($"{name == "Ada"}");
println($"{true != false}");
println($"{'x' == 'x'}");
They do not work on a type you declare, on a union, on an
array, or on any. There is no structural comparison and no way to give a type
one.
pub type Point {
pub field x: i32;
}
let a = new Point { x: 1 };
let b = new Point { x: 1 };
let same = a == b;
To compare two values of your own type, write a method that says what comparing them means.
use std;
pub type Point {
pub field x: i32;
pub field y: i32;
}
impl Point {
pub fn equals(other: Point) -> bool {
return self.x == other.x && self.y == other.y;
}
}
println($"{new Point { x: 1, y: 2 }.equals(new Point { x: 1, y: 2 })}");
To ask which case a union holds, use match.
Logical
&&, || and ! work on bool and produce one. && and ||
short circuit; everything else
evaluates both sides, left before right, before the operator is applied.
use std;
let ready = true;
let done = false;
println($"{ready && !done}");
println($"{done || ready}");
Unary
| Operator | On | Produces |
|---|---|---|
! |
bool |
the negation |
- |
a number | the negation |
+ |
a number | the operand unchanged |
There is no negative literal, so -1 is this - applied to 1. That matters
only where it changes the reading: -a.b is -(a.b).
use std;
let value = 5;
println($"{-value}");
println($"{+value}");
println($"{!(value > 10)}");
Type operators
is asks what a value is and produces a bool.
as performs a conversion that is not implicit.
Both take a type on the right, and both bind looser than arithmetic.
use std;
let boxed: any = 32;
if boxed is i32 {
println($"{boxed as i32}");
}
Assignment
= and the compound operators are the loosest of all, and they group right to
left. Assignment is an expression that produces the value it assigned, which
Assignment covers.
What is not here
There is no conditional operator, no bitwise operator, no shift, no increment or decrement, and no way to give a type an operator of its own. A method is how a type says what an operation on it means.