6

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?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
georch
  • 959
  • 7
  • 18
  • See also [What are the differences between using * and & to compare values for equality?](https://stackoverflow.com/q/52682164/155423) and [Why isn't it possible to compare a borrowed integer to a literal integer?](https://stackoverflow.com/q/40677086/155423). – Shepmaster Jan 08 '19 at 21:59
  • Possible duplicate of [Why isn't it possible to compare a borrowed integer to a literal integer?](https://stackoverflow.com/questions/40677086/why-isnt-it-possible-to-compare-a-borrowed-integer-to-a-literal-integer) – Stargateur Jan 08 '19 at 22:28
  • 1
    *ideally in the standard library* — that's the **only** location. [How do I implement a trait I don't own for a type I don't own?](https://stackoverflow.com/q/25413201/155423). – Shepmaster Jan 09 '19 at 00:15

2 Answers2

9

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.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • 1
    *I can't answer why the standard library implements the "mixed" versions for reference and value for arithmetic operators, but not for comparisons.* => That's actually an easy one. A comparison operator never needs to consume a value, while for an arithmetic operator consuming one of the operand to produce the result value allows eschewing clones (with non-Copy types) which is valuable. You could consume values with comparison operators; but consuming when you don't need to potentially induces inefficiencies, and Rust is about efficiency. – Matthieu M. Jan 09 '19 at 12:04
  • @MatthieuM. I thought about this point, but it still doesn't make sense to me. As you stated yourself, this only makes a difference for types that are _not_ `Copy`, but all of the numerical types in the standard library are `Copy`, so I can't see the point. The only thing it achieves is that you save a few `*`s or `&`s, but the same argument could be used for comparison operators. – Sven Marnach Jan 09 '19 at 15:26
  • The traits were conceived with the ability to use them with Big Integers, matrices, etc... – Matthieu M. Jan 09 '19 at 16:26
  • 1
    @MatthieuM. Hmm, I think we are talking about different things. I'm talking about the trait _implementations_ in the standard library. I do understand the design of the traits. What I don't understand is why there are implementations to support e.g. `&i32 + i32` in the standard library, but not for `&i32 < i32`, which is what the OP is asking about. – Sven Marnach Jan 09 '19 at 17:10
  • Ah! I see what you mean now. Good question indeed. – Matthieu M. Jan 10 '19 at 07:24
1

Because you can have Rem implementation for different types and the core library implements

impl<'a> Rem<&'a i32> for i32 { /* … */ }

This is impossible for PartialOrd and Ord traits, so you need to compare exactly the same types, in this case i32, that is why there is requirement for dereference.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Hauleth
  • 22,873
  • 4
  • 61
  • 112
  • 1
    `PartialOrd` takes a parameter just like `Rem` does, so I don't think it's impossible. It just... isn't done. Possibly there is some coherence reason. – trent Jan 08 '19 at 22:01
  • @trentcl think this is a dupe of the Q I [just linked in the comments](https://stackoverflow.com/questions/40677086/compare-a-borrowed-integer-to-a-literal-integer)? – Shepmaster Jan 08 '19 at 22:03
  • @Shepmaster Those links do not address why it *is* possible to write `number % prime`, which this answers. I'm tempted to flag it but I would be happier if there were another that talks about arithmetic operations – trent Jan 08 '19 at 22:08
  • @trentcl I mean, it’s because someone wrote those implementations, yeah? – Shepmaster Jan 08 '19 at 22:09
  • @trentcl but "why" is just the contrary, the answer said because this is not implemented, so why it work it's because it's implemented. But I agree the duplicate doesn't match at 100%. I think this answer would link the other question too, to make them related. – Stargateur Jan 08 '19 at 22:32
  • @Stargateur You make a good point, but I still don't think it is a duplicate. – trent Jan 09 '19 at 02:48