32

I started learning Rust. I tried this program:

fn main() {
     let a = 5;
     let b = 5.5;
     let k = a + b;
     println!("{}", k);
}

and it is showing this error:

error[E0277]: cannot add a float to an integer
 --> src/main.rs:4:16
  |
4 |      let k = a + b;
  |                ^ no implementation for `{integer} + {float}`
  |
  = help: the trait `std::ops::Add<{float}>` is not implemented for `{integer}`

Is the code wrong?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Jøê Grèéñ
  • 551
  • 1
  • 6
  • 15
  • 2
    Welcome to StackOverflow! Here on SO it's expected to show your own effort for solving the problem. Please refer to [this guide](http://stackoverflow.com/help/how-to-ask) for more information on how to ask on SO. – Lukas Kalbertodt Sep 24 '16 at 14:31
  • 7
    @Enzokie Not quite correct. CodeReview is for working code only. This code isn't working, so in theory the problem is well suited for SO. – Lukas Kalbertodt Sep 24 '16 at 14:32
  • Ok I understood, that seems reasonable. – Enzokie Sep 24 '16 at 14:33
  • @Enzokie Please read [this question on Meta CR](http://meta.codereview.stackexchange.com/q/5777/31562) – Simon Forsberg Sep 24 '16 at 14:36
  • 1
    Please take a second to [review some of the edits](http://stackoverflow.com/posts/39677410/revisions) that we've made to your question. Most importantly, your question should have a **good title** (which are hard to come up with, I know!). – Shepmaster Sep 24 '16 at 14:39
  • 1
    @JøêGrèéñ Because you can't. Rust doesn't let you add integers to floats. – Veedrac Sep 24 '16 at 18:48
  • I think the error message that `Rust` gives is a bit confusing. `Go` has similar behavior and would give error message: `invalid operation: a + b (mismatched types int and float64)` – Akavall Sep 26 '16 at 02:25

1 Answers1

62

The technically correct answer is: because no one has written impl Add<f64> for i32 {}.

The cheeky answer is: because Rust doesn't want you to shoot yourself in the foot.

The longer, potentially more useful answer is...

In computers, integers and floating point numbers both have a limited range, ultimately driven by the number of bits that we use to represent them. In Rust, the default type of an integer that isn't otherwise constrained is a i32, and the default type of a floating point that isn't otherwise constrained is a f64.

Integral types don't allow you to have a fractional part, and floating point types have a limited number of integers they can exactly represent. If Rust let you add these two types, it would be making a decision for you about which piece of data was less important, which is not really the kind of thing you want your systems programming language to do!

Here are the options I can see:

  1. Raise an error, forcing the programmer to pick which data type they need.
  2. Automatically convert both numbers to an integer, discarding any potential fractional values.
  3. Automatically convert both numbers to floating point, improperly representing larger integral values.

Of those choices, only an error is reasonable.

There's also the potential to introduce a type that can precisely handle arbitrary precision. Unfortunately, those types are no longer "cheap" for the processor to operate on, so you'd have to trade off performance.

If the programmer wishes to perform some conversion, then you can cast the value using as or From:

f64::from(a) + b;
a + b as i32 

See also:


Veedrac adds:

[this answer gives] the impression that 0u32 + 0u64 should work, but Rust doesn't do any numeric promotions, even if promotion would be lossless. Also, i32f64 is a lossless promotion, since f64 has a 52-bit mantissa.

While these types of widening promotions would indeed be lossless, they would involve implicitly increasing your memory requirements. What used to only take 32 bits now takes 64 bits, for example. Beyond the memory requirements, there's also semantic considerations. If a value should only require a u8 (0-255), then it doesn't make sense to increase it by a value that might be beyond that range. Knowing that it is appropriate to do such a transformation falls solely on the programmer.

Using From can ensure that you only use lossless number conversion.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 5
    Though this explanation is nice, I think it's misleading. It gives one the impression that `0u32 + 0u64` should work, but Rust doesn't do *any* numeric promotions, even if promotion would be lossless. Also, `i32`→`f64` is a lossless promotion, since `f64` has a 52-bit mantissa. – Veedrac Sep 25 '16 at 04:49
  • 1
    **But why is it so**? **C** lets me `64-bit float` **with a** `32-bit int` – Jøê Grèéñ Sep 25 '16 at 10:32
  • 2
    Rust is usually against implicit conversions. That's just the way it is, but I honestly prefer it that way. – E_net4 Sep 25 '16 at 13:09
  • 12
    @JøêGrèéñ I'm not sure what else I can add, I've given multiple reasons why Rust does not do these implicit conversions. C allows other things that Rust does not, such as dereferencing a NULL pointer, but that doesn't mean it's a good idea that should be perpetuated. I'd suppose your question could be flipped the other way: **Why does C allow adding a `double` to a `int32_t`?** – Shepmaster Sep 25 '16 at 13:49
  • A very nice and detailed explanation. Thank you! – Khoa Tran Aug 04 '20 at 09:41
  • @Veedrac That doesn't guarantee a lossless promotion. Huge integer with small float, or small integer with huge float will still result in a lossful promotion. Rust tries to make you acknowledge that. – zombiesauce Dec 11 '21 at 10:13
  • @zombiesauce `u32` → `u64` and `i32` → `f64` are always lossless. – Veedrac Dec 13 '21 at 16:53
  • @Veedrac Obviously simply converting an `i32` to an `f64` is lossless. I'm talking about the case when an `f64` already exists, has a huge value compared to even `i32::MAX`. In that case, adding any `i32` to that `f64` will quite likely result in lossful promotion. I don't know about you, but I'd like Rust to tell me when I'm about to do such operations where loss can be encountered. It has already once saved me while making game physics, until I had to actually `as` cast it. – zombiesauce Dec 17 '21 at 10:30
  • 1
    @zombiesauce That has nothing to do with the promotion. Rust does not warn you about `f64 + f64`, which has many more lossy operations of much greater magnitude. – Veedrac Dec 17 '21 at 20:45
  • @Veedrac That's at the limit what a language can do. When I'm already in the floating-point-land, I don't need to be reminded but I'd like to be when I transition over from the deterministic integer land to the other one. I don't want to appear as just blindly making up points to defend an arbitrary decision. I am very much frustrated by lossless promotions in cases of `i32` to `i64` or more frequently their unsigned variants. That's something I'd like get fixed, but something like `u32` to `f32` better have me explicitly convert, and I'm glad that integer to float promotion is explicit. – zombiesauce Dec 18 '21 at 12:28