Operators#
Rust’s operator surface covers arithmetic, comparison, logical,
bitwise, reference (& *), range (a..b), assignment, and
the try operator (?). Operators are also traits;
you can implement them for custom types
(std::ops::Add for +, Index for [], etc.).
/types`.
Arithmetic#
Operator |
Meaning |
|---|---|
|
the usual; division between integers truncates toward zero. |
Integer overflow panics in debug and wraps in release.
For explicit handling: checked_add (returns Option),
saturating_add, wrapping_add, overflowing_add.
let n: u8 = 200;
let m: u8 = n.wrapping_add(100); // 44
let opt = n.checked_add(100); // None
let sat = n.saturating_add(100); // 255
Relational#
== != < <= > >= work on any type implementing PartialEq
/ PartialOrd (most stdlib types). Floats are
PartialOrd (not Ord) because of NaN.
1 == 1 // true
"abc" < "abd" // true (lex)
let v = vec![1, 2, 3];
v == vec![1, 2, 3] // true; element-wise
Logical#
&& and || short-circuit; ! is unary. Operate on
bool only; no truthiness.
if x > 0 && !done { /* … */ }
let either = a || b; // a, b: bool
Bitwise#
let a = 0xff_u32 & 0x0f; // 15
let b = 1u32 << 8; // 256
let c = !0u32; // 0xffffffff
let d = 0b1100 ^ 0b1010; // 0b0110
Assignment#
=, plus the compound forms += -= *= /= %= &= |= ^= <<= >>=.
let mut x = 1;
x += 2;
x <<= 1;
Range#
for i in 0..10 { /* 0..=9 */ }
for i in 0..=10 { /* 0..=10 */ }
let sub = &xs[1..3];
Ranges are values themselves (std::ops::Range), so the
operator can name them, pass them, and iterate them later.
The try operator (?)#
? is go-tos for propagating errors. Applied
to a Result: unwrap on Ok, return Err(From::from(e))
on Err. Applied to an Option: unwrap on Some,
return None.
fn load(path: &str) -> std::io::Result<String> {
let s = std::fs::read_to_string(path)?; // bubble up io errors
Ok(s.trim().to_string())
}
fn parse_port(s: &str) -> Option<u16> {
let n = s.parse::<u32>().ok()?;
(n <= 65535).then_some(n as u16)
}
The function’s return type must match the propagated type (or
be convertible via From).
References and deref#
&x borrows; &mut x borrows mutably; *r dereferences.
let n = 10;
let r = &n;
let v = *r; // 10
The dot operator . autoderefs through any number of
references, so s.len() works whether s is String,
&String, &&String.
as#
Explicit primitive cast (numeric, pointer, raw pointer cast).
Use as sparingly because it truncates
silently on out-of-range conversions; TryFrom returns a
Result.
let i: i32 = 1000;
let b: u8 = i as u8; // truncates: 232
let b: u8 = u8::try_from(1000)?; // returns Err
Operator overloading#
Implementing std::ops::Add, Mul, Sub, Neg,
Index, Deref, etc. lets the operator’s types use
operator syntax.
use std::ops::Add;
#[derive(Clone, Copy, Debug)]
struct Vec2 { x: f64, y: f64 }
impl Add for Vec2 {
type Output = Vec2;
fn add(self, other: Vec2) -> Vec2 {
Vec2 { x: self.x + other.x, y: self.y + other.y }
}
}
let v = Vec2 { x: 1.0, y: 2.0 } + Vec2 { x: 3.0, y: 4.0 };
Precedence#
Subset, highest to lowest. The operator parenthesises whenever in doubt.
Group |
Operators |
|---|---|
postfix |
|
unary |
|
|
|
multiplicative |
|
additive |
|
shift |
|
bitwise |
|
relational |
|
equality |
|
logical |
|
range |
|
assignment |
|
References#
Types for the values each operator accepts.
Control flow for
?insideif let/match.Errors for
ResultandOptionwith?.OOP for implementing
Add,Index, etc.