Operators
Expressions are joined by operators. This page goes through all of them family by family; precedence rules are collected at the end.
Arithmetic
print(7 / 2, -7 / 2, 7 % 2, -7 % 2);
print(2 + 3 * 4);
print(2 ** 10, 2 ** 3 ** 2, -2 ** 2);3 -3 1 -1
14
1024 512 -4- Integer division truncates toward zero:
-7 / 2is-3, not-4. The sign of the modulus follows the dividend, so-7 % 2is-1. **is exponentiation and right-associative:2 ** 3 ** 2evaluates as2 ** (3 ** 2), giving 512.- The operand of unary minus spans the whole power:
-2 ** 2parses as-(2 ** 2), giving-4. Write parentheses for(-2) ** 2.
Integer Overflow: Fixed-Width Wraparound
The default contract of integer arithmetic is unchecked fixed-width wraparound: a result that exceeds the width wraps around from the other end, with no error. Adding 1 to the int32 maximum wraps to the minimum; adding 1 to the uint32 maximum wraps to 0:
var m: int32 = 2147483647;
print(m + 1);
var u: uint32 = 4294967295;
print(u + 1);-2147483648
0int64 behaves the same, and the result type stays unchanged. To check for overflow explicitly, use the Checked family on Math: CheckedAdd / CheckedSub / CheckedMul return null on overflow instead of wrapping:
print(Math.CheckedAdd(9223372036854775807, 1) == null);
print(Math.CheckedMul(3037000500, 3037000500) == null);True
TrueFor the literal rules at each width and the Checked family in constant contexts, see Integers.
+ and * on Strings
+ concatenates as soon as one side is a string, stringifying the other side automatically; both directions are legal. * repeats when one side is a string and the other an integer:
print("n=" + 5, 5 + "=n", "ab" * 3);n=5 5=n abababConcatenation inserts no separator; write any space or punctuation into the literal yourself.
Comparison and Equality
All six comparison operators < <= > >= == != return a bool. Ordering comparisons on strings are ordinal, comparing code points character by character:
print("Z" < "a", "ab" == "ab");True TrueThe code point of uppercase Z is smaller than that of lowercase a, so "Z" < "a" holds.
Logical and Bitwise
The operands of &&, ||, and ! must be bools; an integer placed there directly is an error, so bit tests are written as explicit comparisons. The bitwise operators & | ^ ~ << >> act on integers:
print(true && false, true || false, !true);
print(5 & 3, 5 | 3, 5 ^ 3, ~5, 1 << 4);False True False
1 7 6 -6 16Ternary
cond ? a : b is an expression, right-associative, and can nest:
val x = 5;
print(x > 0 ? x > 3 ? "big" : "small" : "not positive");bigThe nesting reads as x > 0 ? (x > 3 ? "big" : "small") : "not positive". When a value-returning branch choice runs deeper than two levels, match is clearer.
Ranges and in
a..b is a half-open range and a..=b is closed; the bounds must be integers. in tests membership:
print(5 in 1..10, 10 in 1..10, 10 in 1..=10);
val r = 1..5;
print(3 in r, r);True False True
True 1..5Ranges are values: store one in a variable and test against it later. in also accepts lists, strings (substring semantics), and other iterable types. The range operators do not associate; 1..2..3 is an outright MS2014 error. Stepped iteration is written for i in 0..10 step 3; see Loops.
Pipelines
The pipeline operator has four forms: a |> f is f(a), passing the left-hand value as an argument to the function on the right. The with-arguments form a |> f(x) is f(a, x). The null-safe pipeline a ?|> f short-circuits to null when the left side is null. The fallible pipeline a |>? f is try? f(a). The propagating pipeline a |>! f is try! f(a):
func Dbl(n: int) -> int => n * 2;
func Add(a: int, b: int) -> int => a + b;
print(21 |> Dbl, 21 |> Add(10));42 31The _ in the target expression is a placeholder: the left-hand value lands where the _ sits (xs |> _[0] and ", ".Join(_) both work). Chained pipelines flow left to right, which suits laying out a series of transformations in reading order. The full story on the four operators and the placeholder is in Pipelines.
Null-Conditional Operators
Nullable types (T?) come with a set of dedicated operators:
| Form | Name | Behavior |
|---|---|---|
o?.m | null-conditional member | The whole expression is null when o is null |
o?[i] | null-conditional index | The same, in index form |
a ?? b | null coalescing | a when non-null, otherwise b |
a ??= b | null coalescing assignment | Assigns only when a is null |
o! | non-null assertion | Asserts non-null; a null at runtime raises MS6045 |
var s: string? = null;
print(s ?? "default", s?.Length ?? -1);
s = "content";
print(s);
var nickname: string? = null;
nickname ??= "Anonymous";
print(nickname);default -1
content
AnonymousArithmetic over nullable values is itself nullable: if either side is null, the whole expression is null.
val a: int? = 2;
val b: int? = null;
print(a + 1, a + b ?? -1);3 -1a + b is null, and ?? -1 supplies the default. Note that ?? binds looser than all arithmetic, so a + b ?? -1 groups as (a + b) ?? -1.
For a fuller treatment of nullable types, see Nullable.
Precedence of with
with is a high-precedence suffix that attaches to the nearest expression on its left, not to the whole calculation:
print(vec2(1, 0) + vec2(2, 3) with { x: 9.0 });vec2(10.0, 3.0)What actually computes is 1,0 + (2,3 with x updated to 9), giving (10, 3). To update the whole sum, add parentheses. Full with usage is in Vectors and Colors.
Custom Operators
Classes and structs can overload operators: declare them with the bare operator form, and the first parameter must be the receiver self. The full set of overloadable operators: + - * / % == != < <= > >= & | ^ ~:
class Vec
{
public var x: int = 0;
public var y: int = 0;
public operator +(self, other: Vec) -> Vec
{
return new Vec() { x: self.x + other.x, y: self.y + other.y };
}
}
func main() -> int
{
val a = new Vec() { x: 1, y: 2 };
val b = new Vec() { x: 3, y: 4 };
val s = a + b;
print(s.x, s.y);
return 0;
}4 6Operator overloads are also the satisfaction surface for protocol operator signatures: when a type impls a protocol containing operator signatures, it must supply a matching overload for each one. Overloading == / != takes over the type’s equality. For the language-wide ruling when nothing is overloaded, see Equality and Hashing. The conversion operator, conversion, is a separate declaration; see Conversions.
Notes
- Assignment and compound assignment (
= += -= *= /= %= **= &= |= ^= <<= >>= ??=) are right-associative;&&=and||=do not exist. - Postfix
i++/i--is statement sugar fori += 1. - Full precedence, lowest to highest: assignment → ternary → pipeline →
||→&&→??→ equality andin→isas→ relational → bitwise or → bitwise xor → bitwise and → shift → range → additive → multiplicative → power → unary → null-conditional →with→ member and call.
