Menu Close

Rust – Operators in Rust

In this article, we are going to understand Operators in Rust with their usages in detail, along with examples.

Operators in Rust can be classified as:

Arithmetic Operators

Basic Arithmetic Operators:

Rust provides the following basic arithmetic operators:

  • Addition (+)
  • Subtraction (-)
  • Multiplication (*)
  • Division (/)
  • Remainder (modulus) (%)

These operators can be used with integer and floating-point number types. Here’s an example of how to use them:

fn main() {

    let a: i32 = 10;

    let b: i32 = 3;


    let addition = a + b;

    let subtraction = a - b;

    let multiplication = a * b;

    let division = a / b;

    let remainder = a % b;


    println!("a + b = {}", addition);

    println!("a - b = {}", subtraction);

    println!("a * b = {}", multiplication);

    println!("a / b = {}", division);

    println!("a % b = {}", remainder);

}

Compound Assignment Operators:

Rust also supports compound assignment operators that combine arithmetic operations with assignment. These operators are useful for updating a variable’s value while applying an arithmetic operation. The compound assignment operators include:

  • +=: Addition and assignment
  • -=: Subtraction and assignment
  • *=: Multiplication and assignment
  • /=: Division and assignment
  • %=: Remainder (modulus) and assignment

Here’s an example using compound assignment operators:

fn main() {

    let mut a: i32 = 10;

    let b: i32 = 3;


    a += b; // Equivalent to a = a + b

    println!("a += b; a = {}", a);


    a -= b; // Equivalent to a = a - b

    println!("a -= b; a = {}", a);


    a *= b; // Equivalent to a = a * b

    println!("a *= b; a = {}", a);


    a /= b; // Equivalent to a = a / b

    println!("a /= b; a = {}", a);


    a %= b; // Equivalent to a = a % b

    println!("a %= b; a = {}", a);

}

Unary Negation Operator:

Rust includes the unary negation operator (-), which negates the value of a number. This operator can be used with both integers and floating-point numbers. Here’s an example:

fn main() {

    let a: i32 = 10;

    let negation = -a;


    println!("-a = {}", negation);

}

Numeric Type Casting:

While Rust’s arithmetic operators work seamlessly within the same numeric type, you may need to perform operations on values of different types. In such cases, Rust requires explicit type casting. You can use the as keyword to cast a value from one numeric type to another:

fn main() {

    let a: i32 = 10;

    let b: f64 = 3.5;


    let multiplication = a as f64 * b;


    println!("a * b = {}", multiplication);

}

In this example, we cast the integer a to a f64 floating-point number before performing the multiplication.

Comparison Operators

Comparison operators are essential in programming languages as they allow developers to compare values and make decisions based on the outcome of the comparison. Rust offers a variety of comparison operators for working with numeric, boolean, and other comparable types.

Basic Comparison Operators:

Rust provides the following basic comparison operators:

  • Equal to (==)
  • Not equal to (!=)
  • Less than (<)
  • Greater than (>)
  • Less than or equal to (<=)
  • Greater than or equal to (>=)

These operators can be used with integer and floating-point number types, characters, and other types implementing the PartialOrd or Ord traits. Here’s an example of using comparison operators with numbers:

fn main() {

    let a: i32 = 10;

    let b: i32 = 5;


    println!("a == b: {}", a == b);

    println!("a != b: {}", a != b);

    println!("a < b: {}", a < b);

    println!("a > b: {}", a > b);

    println!("a <= b: {}", a <= b);

    println!("a >= b: {}", a >= b);

}

Comparing Floating-Point Numbers:

When comparing floating-point numbers, keep in mind that due to the nature of their representation, some values may not be exactly equal. Therefore, it’s better to compare them within a certain range, known as an epsilon, to avoid unexpected results. Here’s an example:

fn main() {

    let a: f64 = 0.1 + 0.2;

    let b: f64 = 0.3;

    let epsilon: f64 = 1e-9;


    println!("a == b: {}", (a - b).abs() < epsilon);

}

Comparing Custom Types:

To use comparison operators with custom types, you need to implement the PartialOrd or Ord traits. The PartialOrd trait provides support for partial ordering, where some values might not have a defined order (e.g., floating-point numbers with NaN values). The Ord trait requires a total ordering, meaning that any two values of the type can be compared.

Here’s an example of implementing the PartialOrd and Ord traits for a custom type:

Example 1:

#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]

struct Point {

    x: i32,

    y: i32,

}


fn main() {

    let p1 = Point { x: 1, y: 2 };

    let p2 = Point { x: 2, y: 1 };


    println!("p1 == p2: {}", p1 == p2);

    println!("p1 != p2: {}", p1 != p2);

    println!("p1 < p2: {}", p1 < p2);

    println!("p1 > p2: {}", p1 > p2);

    println!("p1 <= p2: {}", p1 <= p2);

    println!("p1 >= p2: {}", p1 >= p2);

}

In this example, we define a Point struct with x and y fields, and we use the derive attribute to automatically implement the PartialEq, Eq, PartialOrd, and Ord traits.

Example 2:

use std::cmp::Ordering;

#[derive(Debug, PartialEq, Eq)]

struct MyType {

    value: i32,

}


impl PartialOrd for MyType {

    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {

        Some(self.cmp(other))

    }

}


impl Ord for MyType {

    fn cmp(&self, other: &Self) -> Ordering {

        self.value.cmp(&other.value)

    }

}


fn main() {

    let a = MyType { value: 10 };

    let b = MyType { value: 5 };


    println!("a > b: {}", a > b);

}

Logical Operators

Logical operators are crucial in programming languages as they enable developers to perform operations on boolean values and create more complex conditions. Rust provides several logical operators for working with boolean values, which are helpful in control flow statements like if, else if, and while. In this article, we will examine Rust’s logical operators in detail and provide examples to demonstrate their usage.

Basic Logical Operators:

Rust provides the following basic logical operators:

  • Logical AND (&&)
  • Logical OR (||)
  • Logical NOT (!)

These operators work with boolean values (true or false) and return a boolean result. Here’s an example of how to use them:

fn main() {

    let a: bool = true;

    let b: bool = false;


    println!("a && b: {}", a && b);

    println!("a || b: {}", a || b);

    println!("!a: {}", !a);

}

Short-circuit Evaluation:

Rust’s logical AND and logical OR operators use short-circuit evaluation, meaning that they only evaluate the second operand if necessary. This can be particularly useful in scenarios where evaluating the second operand might cause an error or be computationally expensive. Here’s an example:

fn main() {

    let a = true;

    let b = false;

    let expensive_operation = || {

        println!("Expensive operation called");

        true

    };


    println!("Using AND operator");

    if a && expensive_operation() {

        println!("Both conditions are true");

    }


    println!("Using OR operator");

    if a || expensive_operation() {

        println!("At least one condition is true");

    }

}

In this example, the expensive_operation function is only called when necessary.

Combining Logical Operators:

You can combine logical operators to create more complex conditions. Here’s an example:

fn main() {

    let a: i32 = 10;

    let b: i32 = 5;

    let c: i32 = 7;


    if a > b && a > c {

        println!("a is the largest number");

    } else if b > a && b > c {

        println!("b is the largest number");

    } else {

        println!("c is the largest number");

    }

}

Using Logical Operators with Non-Boolean Types:

Although Rust’s logical operators are primarily designed for boolean values, you can also use them with non-boolean types by leveraging the bool method from the From trait. However, this requires an explicit conversion to a boolean value. Here’s an example:

fn main() {

    let a: i32 = 10;

    let b: i32 = 0;


    if bool::from(a != 0) && bool::from(b != 0) {

        println!("Both a and b are non-zero");

    } else {

        println!("At least one of a and b is zero");

    }

}

Note that using logical operators with non-boolean types directly is not idiomatic Rust, and it’s better to use boolean expressions in most cases.

Bitwise Operators

Bitwise operators allow developers to perform operations at the bit level, which can be helpful for low-level programming, optimization, and working with binary data. Rust provides several bitwise operators for working with integers.

Rust provides the following basic bitwise operators:

  • Bitwise AND (&)
  • Bitwise OR (|)
  • Bitwise XOR (^)
  • Bitwise NOT (!)
  • Left shift (<<)
  • Right shift (>>)

Bitwise AND (&):

The bitwise AND operator (&) compares each bit of the first integer with the corresponding bit of the second integer. If both bits are 1, the corresponding result bit is set to 1; otherwise, the result bit is set to 0.

Example:

fn main() {

    let a: u8 = 0b10110011;

    let b: u8 = 0b11001010;

    let result = a & b;


    println!("a: {:08b}, b: {:08b}, a & b: {:08b}", a, b, result);

}

Output:

a: 10110011, b: 11001010, a & b: 10000010

Bitwise OR (|):

The bitwise OR operator (|) compares each bit of the first integer with the corresponding bit of the second integer. If either bit is 1, the corresponding result bit is set to 1; otherwise, the result bit is set to 0.

Example:

fn main() {

    let a: u8 = 0b10110011;

    let b: u8 = 0b11001010;

    let result = a | b;


    println!("a: {:08b}, b: {:08b}, a | b: {:08b}", a, b, result);

}

Output:

a: 10110011, b: 11001010, a | b: 11111011

Bitwise XOR (^):

The bitwise XOR operator (^) compares each bit of the first integer with the corresponding bit of the second integer. If the bits are different, the corresponding result bit is set to 1; otherwise, the result bit is set to 0.

fn main() {

    let a: u8 = 0b10110011;

    let b: u8 = 0b11001010;

    let result = a ^ b;


    println!("a: {:08b}, b: {:08b}, a ^ b: {:08b}", a, b, result);

}

Output:

a: 10110011, b: 11001010, a ^ b: 01111001

Bitwise NOT (!):

The bitwise NOT operator (!) inverts each bit of an integer. If a bit is 1, it becomes 0; if a bit is 0, it becomes 1.

Example:

fn main() {

    let a: u8 = 0b10110011;

    let result = !a;


    println!("a: {:08b}, !a: {:08b}", a, result);

}

Output:

a: 10110011, !a: 01001100

Left Shift Operator (<<):

The left shift operator (<<) shifts the bits of an integer to the left by a specified number of positions. Any bits that are shifted beyond the leftmost position are discarded, while new bits are filled with zeros on the right.

Example:

fn main() {

    let a: u8 = 0b10110011;

    let shift_amount = 2;

    let result = a << shift_amount;


    println!("a: {:08b}, a << {}: {:08b}", a, shift_amount, result);

}

Output:

a: 10110011, a << 2: 11001100

Right Shift Operator (>>):

The right shift operator (>>) shifts the bits of an integer to the right by a specified number of positions. Any bits that are shifted beyond the rightmost position are discarded, while new bits are filled with zeros on the left side for unsigned integers or with the sign bit (0 for positive and 1 for negative) for signed integers.

Example (unsigned integer):

fn main() {

    let a: u8 = 0b10110011;

    let shift_amount = 2;

    let result = a >> shift_amount;


    println!("a: {:08b}, a >> {}: {:08b}", a, shift_amount, result);

}

Output:

a: 10110011, a >> 2: 00101100

In the example above, the bits of the unsigned integer ‘a’ are shifted two positions to the right. The two rightmost bits (11) are discarded, and two zeros are added on the left side.

Example (signed integer):

fn main() {

    let a: i8 = -0b10110011;

    let shift_amount = 2;

    let result = a >> shift_amount;


    println!("a: {:08b}, a >> {}: {:08b}", a, shift_amount, result);

}

Output:

a: 10110011, a >> 2: 11101100

In this example, the bits of the signed integer ‘a’ are shifted two positions to the right. The two rightmost bits (11) are discarded, and the sign bit (1) is used to fill the new bits on the left side.

Keep in mind that the shift amount must be less than the number of bits in the integer type. If the shift amount is equal to or greater than the number of bits, the behavior is undefined. To avoid this, you can use the wrapping_shr method provided by Rust integer types:

Example:

fn main() {

    let a: u8 = 0b10110011;

    let shift_amount = 10; // This value is greater than 8, the number of bits in u8.

    let result = a.wrapping_shr(shift_amount);


    println!("a: {:08b}, a.wrapping_shr({}): {:08b}", a, shift_amount, result);

}

Output:

a: 10110011, a.wrapping_shr(10): 00101100

In this example, the wrapping_shr method is used, which wraps the shift amount to fit the number of bits in the integer type. It calculates the shift amount as shift_amount % number_of_bits (10 % 8 = 2), and the result is the same as in the first example.

Compound Bitwise Assignment Operators:

Rust also supports compound bitwise assignment operators that combine bitwise operations with assignment. These operators are useful for updating a variable’s value while applying a bitwise operation. The compound bitwise assignment operators include:

  • &=: Bitwise AND and assignment
  • |=: Bitwise OR and assignment
  • ^=: Bitwise XOR and assignment
  • <<=: Left shift and assignment
  • >>=: Right shift and assignment

Here’s an example using compound bitwise assignment operators:

fn main() {

    let mut a: u8 = 0b1010_1010;

    let b: u8 = 0b0101_0101;


    a &= b; // Equivalent to a = a & b

    println!("a &= b; a = {:08b}", a);


    a |= b; // Equivalent to a = a | b

    println!("a |= b; a = {:08b}", a);


    a ^= b; // Equivalent to a = a ^ b

    println!("a ^= b; a = {:08b}", a);


    a <<= 2; // Equivalent to a = a << 2

    println!("a <<= 2; a = {:08b}", a);


    a >>= 2; // Equivalent to a = a >> 2

    println!("a >>= 2; a = {:08b}", a);

}

To check more Go related articles. Pls click given below link:

https://en.wikipedia.org/wiki/Rust_(programming_language)

https://www.techieindoor.com/rust-constant-in-rust/

Posted in Rust

Leave a Reply

Your email address will not be published. Required fields are marked *