Operators in Hare
Learn about arithmetic, comparison, logical, bitwise, and Hare-specific operators including error assertion and propagation
Operators are the building blocks of expressions in any programming language. As an imperative systems language with a static, strong type system, Hare offers a familiar set of C-style operators — but with key differences that reflect its design philosophy of explicitness and safety. There are no implicit type conversions, no operator overloading, and arithmetic must be performed on operands of the same type.
In addition to the standard arithmetic, comparison, logical, and bitwise operators you’d expect, Hare introduces two distinctive operators tied to its tagged-union-based error handling: the error assertion operator (!) and the error propagation operator (?). Together with the explicit as cast operator, these give Hare its characteristic style — terse where it counts, but never silently doing something surprising.
This tutorial covers the operator categories you’ll use most often, then highlights the Hare-specific operators that distinguish the language from C.
Arithmetic and Comparison Operators
Hare’s arithmetic operators behave like C’s, with one important caveat: both operands must be the same type. There are no implicit numeric conversions — you must cast explicitly with the as operator when mixing types.
Create a file named operators.ha:
| |
The as keyword is Hare’s type cast operator — expr as type rather than the C-style (type)expr. Without the cast, dividing an int by an f64 would be a compile error. (Don’t confuse as with :, the type assertion operator used to extract a specific type from a tagged union.)
Logical and Bitwise Operators
Hare distinguishes between logical operators (which only work on bool) and bitwise operators (which work on integer types). The logical operators && and || short-circuit just as they do in C.
Create a file named bitwise.ha:
| |
Note the {:04b} format specifier — Hare’s fmt module supports format flags similar to Rust’s, where b means binary and 04 means pad to four digits with zeros.
Compound Assignment Operators
Hare supports compound assignment operators that combine an arithmetic or bitwise operation with assignment. They require the target to be a mutable binding declared with let rather than const.
Create a file named assignment.ha:
| |
Error Handling and Pointer Operators
Hare’s most distinctive operators are tied to its tagged-union error handling system. The ! operator asserts that an operation will not return an error (aborting the program if it does), while ? propagates an error to the caller. The unary & takes the address of a value, and * dereferences a pointer.
Create a file named hare_specific.ha:
| |
The function signature (int | strconv::error) is a tagged union — the function returns either an int or a strconv::error. The ? operator inside parse_double says: “if this is an error, return it from the enclosing function.” The ! operator at the call site says: “I’m certain this won’t fail, so just give me the value.”
Running with Docker
The Alpine Linux edge repository packages Hare, so we install it on first run:
| |
Expected Output
Running operators.ha:
a + b = 22
a - b = 12
a * b = 85
a / b = 3
a % b = 2
-a = -17
a == b : false
a != b : true
a < b : false
a > b : true
a <= b : false
a >= b : true
a as f64 / f = 5.666666666666667
Running bitwise.ha:
t && f = false
t || f = true
!t = false
x & y = 1000
x | y = 1110
x ^ y = 0110
~x = 11110011
x << 2 = 00110000
x >> 1 = 0110
Running assignment.ha:
after += 5 : 15
after -= 3 : 12
after *= 2 : 24
after /= 4 : 6
after %= 4 : 2
after &= : 1000
after |= : 1001
after ^= : 0110
after <<= 1 : 00001100
after >>= 2 : 0011
Running hare_specific.ha:
doubled = 42
value = 100
*ptr = 100
value = 250 (after *ptr = 250)
2 + 3 * 4 > 10 && 2 < 3 = true
Operator Precedence Summary
From highest to lowest precedence (a partial list of the most common operators):
- Postfix:
()(call),[](index),.(field),?,! - Unary prefix:
-,!,~,&(address-of),*(dereference) - Cast:
as,is,:(type assertion) - Multiplicative:
*,/,% - Additive:
+,- - Shift:
<<,>> - Bitwise AND:
& - Bitwise XOR:
^ - Bitwise OR:
| - Comparison:
<,>,<=,>= - Equality:
==,!= - Logical AND:
&& - Logical OR:
|| - Assignment:
=,+=,-=, etc.
When in doubt, use parentheses to make intent explicit.
Key Concepts
- No implicit conversions — operands of arithmetic and comparison operators must share the same type; use
expr: typeto cast explicitly - No operator overloading — every operator has a fixed meaning, making code unambiguous
- Logical vs bitwise —
&&/||/!operate onbooland short-circuit;&/|/^/~operate on integers - Error assertion (
!) — asserts an operation will not fail; aborts the program with a trace if it does - Error propagation (
?) — returns the error to the caller if the operation fails, otherwise unwraps the success value - Pointer operators —
&takes an address,*dereferences; pointers are non-null by default - Mutability matters — compound assignment (
+=,-=, etc.) requiresletbindings;constbindings cannot be reassigned - Modulo on signed integers —
%follows the sign of the dividend, matching C’s behavior
Next Steps
Continue to Control Flow to see how comparison and logical operators combine with if, switch, and Hare’s match expression for tagged unions.
Comments
Loading comments...
Leave a Comment