Milack27
Milack27

Reputation: 1689

Why can't I compare two integers of different types?

let x: i32 = 4;
let y: i16 = 4;

println!("{}", x == y);

When compiling the snippet above, the compiler prints the following error:

error[E0308]: mismatched types
 --> src/main.rs:5:25
  |
5 |     println!("{}", x == y);
  |                         ^ expected i32, found i16

It seems that PartialEq is not implemented for different types of integers. The same happens between f32 and f64, and for PartialOrd as well. Is there a reason for that? Is it intended to be implemented in future versions of Rust?

Upvotes: 14

Views: 15291

Answers (3)

kangalio
kangalio

Reputation: 673

EDIT: I submitted an RFC and, among out a host of misguided answers, thomcc provided this valuable comment:

I think the inference issues make this a non-starter. I'd expect this to break all the rust code where a variable of numeric type is compared against a number literal (for example, a == 0). We had a similar (but less extreme) issue in std::simd, and it broke a ton of code (even things that didn't care at all about simd).

Type inference shouldn't be affected by this change, because the generic parameter on PartialOrd and PartialEq is defaulted to Self. Therefore, expressions like 1 == 1_u8 will continue to work by correctly inferring the first literal to be a u8.

Sadly, I don't believe this is how type inference works, at least not at the moment (and my understanding is that extending it to handle defaults the way some folks expect requires some nontrivial design work, at the very least).

So, this is the real answer why integers of different types cannot be compared


I researched and found a couple arguments. Comparing differently sized integers...

  1. requires implicit casts, which can easily be misused and cause bugs
  2. is hard to define properly / can yield unexpected results in edge cases
  3. requires non-trivial logic (e.g. sign checks or zero extension) and may have unacceptable performance implications for an integral type operation
  4. may hint at a design problem in the code
  5. adding (Partial)Eq/Ord implementations for each combination of all 12 integer types is 264 implementations, which may impact compile-times

Responses:

  1. is moot: there would be explicit Eq/Ord implementations for differently sized integers.

  2. is also moot, since the mathematical definition of ordering is well-defined and intuitive.

  3. can be tested with a benchmark. On my Intel i5-8600k, the u64 < i64 check with a sign check runs 0.1 nanoseconds slower than the native u64 < u64 check, which is negligible.

  4. may be valid, albeit very subjective and hard to proove.

  5. this could be tested, although I don't believe it will be a problem:

    • the standard library is pre-compiled
    • there are thousands of trait implementations in the standard library already (e.g. 422 on u8 alone
    • there's precedent for trait implementations on combinations of integer types: From, TryFrom, Shl(Assign), Shr(Assign)

From the arguments I could find, I currently believe allowing comparing differently sized integers is a net positive for Rust.

The algorithms have already been implemented by orlp in the num-ord crate

Upvotes: 1

Matthieu M.
Matthieu M.

Reputation: 300349

There are many integral types, in Rust:

  • i8, i16, i32, i64 and i128,
  • u8, u16, u32, u64 and u128,
  • isize,
  • usize.

In some cases, mixed arithmetic or comparisons would have an obvious implementation as a lossless conversion is possible in one direction:

  • i<x> can always be converted to i<y> if x < y,
  • u<x> can always be converted to u<y> if x < y,
  • u<x> can always be converted to i<y> if x < y.

Some conversions, however, are not obvious or not portable:

  • i<x> cannot be converted to u<y> no matter what the respective values of x and y are,
  • isize and usize have a platform specific size anywhere, as small as 16 bits but as large as 64 bits.

Therefore, since Rust is not keen on overflows or underflows, it is unlikely that arbitrary mixed arithmetic or comparisons will ever be implemented.

A restricted subset could be implemented, but then two questions are raised:

  • isn't a restricted subset unergonomic?
  • isn't using mixed types a sign of design issue? In the same vein that quantities should have units, quantities should probably have a known magnitude.

Upvotes: 11

ljedrz
ljedrz

Reputation: 22273

It is expected behaviour - Rust is a strongly typed language and it doesn't perform implicit casts between integers of different types.

I don't think this will ever change in the future, as it would be a potential source of bugs that are notoriously difficult to find.

You need to be explicit and watch out for potential caveats of numeric casts (truncation etc.):

let x: i32 = 4;
let y: i16 = 4;

println!("{}", x == y as i32);

Upvotes: 9

Related Questions