RUST: Operators in Rust

Rust provides a complete set of arithmetic, bitwise, logical, and comparison operators. Understanding operator precedence, the absence of increment/decrement operators, and how Rust handles integer and float math sets a solid foundation for writing correct expressions.

Operators are the building blocks of expressions in any programming language. Rust's operator set is largely familiar to developers from C, C++, or Java — with a few notable differences that reflect Rust's focus on explicitness and safety.


Arithmetic Operators

Rust supports the standard arithmetic operators for numeric types:

OperatorOperationExample
+Addition2 + 35
-Subtraction5 - 23
*Multiplication4 * 312
/Division10 / 33 (integer)
%Remainder (modulo)13 % 31

rust
fn operators() {
    let mut a = 2 + 3 * 4; // Follows standard precedence: * before +
    println!("{}", a);     // 14

    // No ++ or -- operators in Rust. Use += 1 instead.
    a += 1;  // a = 15
    a -= 1;  // a = 14
    a *= 2;  // a = 28
    a /= 2;  // a = 14
    a %= 3;  // a = 2

    println!("remainder of 13 / 3 = {}", 13 % 3); // 1
}

No `++` or `--` Operators

Rust deliberately does not have the ++ (increment) and -- (decrement) operators found in C, C++, and Java. Instead, you must use explicit compound assignment:

rust
let mut x = 5;
x += 1; // increment
x -= 1; // decrement

This avoids ambiguity between pre-increment and post-increment (++x vs x++), a common source of subtle bugs in C/C++.


Integer Division and Remainder

Integer division in Rust truncates toward zero, consistent with most languages:

rust
println!("{}", 7 / 2);    //  3 (truncated, not 3.5)
println!("{}", -7 / 2);   // -3 (truncates toward zero, not -4)
println!("{}", 7 % 2);    //  1
println!("{}", -7 % 2);   // -1 (remainder has the sign of the dividend)


Exponentiation

Rust has no ** operator for exponentiation. Instead, it provides methods on numeric types:

rust
fn power_examples() {
    let mut a: i32 = 5;

    // Integer exponentiation
    let a_cubed = i32::pow(a, 3); // a^3
    println!("a cubed = {}", a_cubed); // 125

    // Float exponentiation
    let b = 2.5f64;
    let b_cubed = f64::powi(b, 3);                    // integer exponent
    let b_to_pi = f64::powf(b, std::f64::consts::PI); // float exponent
    println!("{} cubed = {}", b, b_cubed);             // 2.5 cubed = 15.625
    println!("{}^pi = {}", b, b_to_pi);                // 2.5^pi = 17.789...
}

MethodTypeDescription
i32::pow(base, exp)IntegerInteger base, integer exponent
f64::powi(base, exp)FloatFloat base, integer exponent (faster)
f64::powf(base, exp)FloatFloat base, float exponent

Compound Assignment Operators

Rust supports the full set of compound assignment operators:

OperatorEquivalentExample
+=a = a + ba += 5
-=a = a - ba -= 5
*=a = a * ba *= 2
/=a = a / ba /= 2
%=a = a % ba %= 3

Bitwise Operators

Bitwise operators work on the individual bits of integer values. They are only available on integer types in Rust:

OperatorOperationExample
`\`Bitwise OR`1 \23`
&Bitwise AND5 & 31
^Bitwise XOR5 ^ 36
!Bitwise NOT!5 (inverts all bits)
<<Left shift1 << 101024
>>Right shift1024 >> 101

rust
fn bitwise_examples() {
    // OR: 01 | 10 = 11 = 3 in decimal
    let c = 1 | 2;
    println!("1 | 2 = {}", c); // 3

    // AND: 0101 & 0011 = 0001 = 1
    let d = 5 & 3;
    println!("5 & 3 = {}", d); // 1

    // XOR: 0101 ^ 0011 = 0110 = 6
    let e = 5 ^ 3;
    println!("5 ^ 3 = {}", e); // 6

    // Left shift: multiply by 2^n
    let two_to_power_10 = 1 << 10;
    println!("2^10 = {}", two_to_power_10); // 1024

    // Right shift: divide by 2^n
    let half = 1024u32 >> 1;
    println!("1024 >> 1 = {}", half); // 512
}

Bit Shifting Visualization

text
Left shift (<<):
  1 = 0000 0001
  1 << 3 = 0000 1000 = 8

Right shift (>>):
  8 = 0000 1000
  8 >> 3 = 0000 0001 = 1

Bitwise compound assignments also work: |=, &=, ^=, <<=, >>=.


Logical Operators

Logical operators work on boolean values and produce boolean results:

OperatorOperationNotes
&&Logical ANDShort-circuits: if left is false, right not evaluated
`\\`Logical ORShort-circuits: if left is true, right not evaluated
!Logical NOTNegates a boolean

rust
fn logical_examples() {
    let x = true;
    let y = false;

    println!("x && y = {}", x && y); // false
    println!("x || y = {}", x || y); // true
    println!("!x = {}", !x);         // false

    // Short-circuit evaluation
    let a = 5;
    let b = 0;
    if b != 0 && a / b > 2 {  // a / b is never evaluated because b != 0 is false
        println!("quotient > 2");
    }
}


Comparison Operators

Comparison operators evaluate to boolean values:

OperatorMeaningExample
==Equal to5 == 5true
!=Not equal to5 != 4true
<Less than3 < 5true
<=Less than or equal5 <= 5true
>Greater than7 > 3true
>=Greater than or equal3 >= 3true

rust
fn comparison_examples() {
    use std::f64::consts::PI;

    let pi_less_4 = PI < 4.0;
    println!("pi < 4.0: {}", pi_less_4); // true

    let x = 5;
    let x_is_5 = x == 5;
    println!("x == 5: {}", x_is_5); // true

    // Comparison can be stored in variables
    let a = 10;
    let b = 20;
    let a_lt_b: bool = a < b;
    println!("a < b: {}", a_lt_b); // true
}


Operator Precedence

Rust follows standard mathematical operator precedence. From highest to lowest:

text
Precedence (highest to lowest):
1. Unary:      !  -  *  &
2. Power:      (no built-in ** operator)
3. Multiply:   *  /  %
4. Add:        +  -
5. Shift:      <<  >>
6. Bitwise:    &  ^  |
7. Compare:    ==  !=  <  <=  >  >=
8. Logical:    &&
9. Logical:    ||
10. Assignment: =  +=  -=  etc.

rust
let result = 2 + 3 * 4; // 14, not 20 (multiplication before addition)
let result = (2 + 3) * 4; // 20 (parentheses override precedence)

When in doubt, use parentheses to make intent explicit. This is especially important in complex boolean expressions.


Type Compatibility and Casting

Rust does not perform implicit type coercion between numeric types. You must use the as keyword to cast between types:

rust
let x: i32 = 100;
let y: f64 = x as f64; // explicit cast required
let z: u8 = 255i32 as u8; // truncating cast — use carefully

println!("{} as f64 = {}", x, y); // 100 as f64 = 100
println!("300 as u8 = {}", 300i32 as u8); // 44 (wraps: 300 - 256 = 44)

Unlike C, truncating casts are defined behavior in Rust (they wrap modulo 2^N), but they can still produce unexpected values. Use checked arithmetic methods when you need to detect truncation.


Complete Example

rust
fn main() {
    // Arithmetic
    let mut a = 2 + 3 * 4;
    println!("2 + 3 * 4 = {}", a); // 14
    a += 1;
    println!("after += 1: {}", a); // 15
    println!("13 % 3 = {}", 13 % 3); // 1

    // Exponentiation
    let a_cubed = i32::pow(a, 3);
    println!("{} cubed = {}", a, a_cubed);

    // Float power
    let b = 2.5f64;
    let b_cubed = f64::powi(b, 3);
    let b_to_pi = f64::powf(b, std::f64::consts::PI);
    println!("{} cubed = {}, {}^pi = {}", b, b_cubed, b, b_to_pi);

    // Bitwise
    let c = 1 | 2;
    println!("1 | 2 = {}", c); // 3
    let two_to_power_10 = 1 << 10;
    println!("2^10 = {}", two_to_power_10); // 1024

    // Comparison and logical
    let pi_less_4 = std::f64::consts::PI < 4.0;
    println!("pi < 4.0: {}", pi_less_4); // true
    let x = 5;
    let x_is_5 = x == 5;
    println!("x == 5: {}", x_is_5); // true
}


Conclusion

Rust's operators are deliberately straightforward. The absence of ++ and -- removes subtle bugs. The requirement for explicit casts prevents silent data loss. The lack of an exponentiation operator (**) is replaced by clear method calls that distinguish integer from float exponentiation.

Combined with Rust's static type system, operators in Rust give you the expressive power of any systems language while eliminating the class of bugs that come from implicit conversions and undefined behavior.