Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I need to dereference a variable when comparing it but not when doing arithmetic?

Tags:

operators

rust

I have the following code:

fn example(known_primes: &[i32], number: i32, prime: i32, limit: i32) {
    let mut is_prime = true;

    for prime in known_primes {
        if number % prime == 0 {
            is_prime = false;
            break;
        }
        if *prime > limit {
            break;
        }
    }
}

Why do I need to dereference prime in the second condition (*prime > limit), when I don't need to do so in the first one (number % prime == 0)?

Both % and < are operators that take two numbers and return something. The only difference seems to be in what they return (a number vs. a boolean). While Why isn't it possible to compare a borrowed integer to a literal integer? does explain what would be required to make the code work (implementations for all overloads, ideally in the standard library), it does not say why it does work for a % b. Is there a fundamental difference between these operators? Or is it just not implemented yet?

like image 613
georch Avatar asked Jan 08 '19 21:01

georch


1 Answers

Comparison operators actually do behave differently than arithmetic operators. The difference becomes obvious when looking at the trait definitions. As an example, here is the PartialEq trait

pub trait PartialEq<Rhs = Self>
where
    Rhs: ?Sized,
{
    fn eq(&self, other: &Rhs) -> bool;
    fn ne(&self, other: &Rhs) -> bool { ... }
}

and the Add trait

pub trait Add<RHS = Self> {
    type Output;
    fn add(self, rhs: RHS) -> Self::Output;
}

We can see that comparison traits take the operands by reference, while the arithmetic traits take the operands by value. This difference is reflected in how the compiler translates operator expressions:

a == b   ==>   std::cmp::PartialEq::eq(&a, &b)
a + b    ==>   std::ops::Add::add(a, b)

The operands of comparisons are evaluated as place expressions, so they can never move values. Operands of arithmetic operators, on the other hand, are evaluated as value expressions, so they are moved or copied depending on whether the operand type is Copy.

As a result of this difference, if we implement PartialEq for the type A, we can not only compare A and A, but also &A and &A by virtue of deref coercions for the operands. For Add on the other hand we need a separate implementation to be able to add &A and &A.

I can't answer why the standard library implements the "mixed" versions for reference and value for arithmetic operators, but not for comparisons. I can't see a fundamental reason why the latter can't be done.

like image 156
Sven Marnach Avatar answered Nov 15 '22 09:11

Sven Marnach