Question about float and double behaviour

fn main() {
      let x = 1.23;
      println!("{}", x.ceil())
}

From what i gather from the Oreilly book Programming Rust, if the compiler cannot decide whether to infer x as f32 or f64 it will by default infer it as f64. I don’t understand why this code will not compile since it can infer x as f64

error[E0599]: no method named ceil found for type {float} in the current scope

Unlike this block of code which works according to my understanding where compiler infers x as f64 and compiles

use std::ops::Add;
fn main() {
      let x = 1.23;
      println!("{}", x.add(2.34))
}
1 Like

The difference between the two is that .add is from a trait, so the compiler can look at all the implementations of Add<f64>, and use that to determine what x should be.

.ceil() is just an inherent method, so the compiler doesn’t look at literally every type ever to find things it could be.

(Certainly {float} means it could potentially do something different here, but today it doesn’t.)

1 Like

Doesn’t Rust compiler infer that it is f64 simply from the shape of the input <a>.<b> instead of from the method it is calling ceil()?

I am guessing that is what is happening here

fn main() {
  let x = 1.23;
  println!("{}", x)
}

Doesn’t Rust compiler infer that it is f64 simply from the shape of the input <a>.<b> instead of from the method it is calling ceil() ?

let a = 1.0;
let b = 1.0;
let c = 1.0_f32;
let d = 1.0;
println!("{}", a + b + c + d);

All of those variables are f32, even though most of them are of the form <a>.<b>.

Another fun example:

struct X;
struct Y;

impl ::std::ops::Add<f64> for X {
    type Output = f64; 
    fn add(self, other: f64) -> f64 { other }
}

impl ::std::ops::Add<f32> for Y {
    type Output = f32; 
    fn add(self, other: f32) -> f32 { other }
}

fn main() {
    println!("{:?}", X + 1.0);  // This 1.0 is an f64
    println!("{:?}", Y + 1.0);  // This 1.0 is an f32
}

I believe those variables are f32 because c is defined to be f32 and a + b + c + d cause them to be all f32. If c was 1.0 i believe compiler would have inferred them to be f64 by default when it does not have a clue to choose between the two.

I might be wrong. I just started learning Rust. May i know how can you tell a variable’s type. I know in Haskell REPL for example you can tell by using :t for example

Same in Rust.

Two options:

  • Try an IDE which can show types. IntelliJ has decent type annotations, and the RLS is never wrong.
  • Still my old favorite: Deliberately cause a type error

This is correct, though my point was to show that type inference plays a role in determining the type of 1.0.

Since you mention Haskell, it’s worth mentioning that there’s some similarity between rust’s so-called {float} and the type of floating literals in Haskell (Floating a => a). I mean, {float} is a total hack, but as far as type inference and unification is concerned, it seems to behave a lot like a type parameter with a trait bound. With heaps of pixie dust.

1 Like

Oh didn’t know Rust had a REPL

Yeah, I misread the question as asking how you tell the compiler the type of a variable:

let x = something()

vs

let x : i32 = something();

My bad.