Failing to infer type for sum()

Hello,

This does not compile, complaining that the type parameter for sum() is not known. But why can it not be infered? Can it possibly be anything other than f64?

fn main() {
    let x: f64 = 1.0;
    let y: f64 = 2.0;
    let z: f64 = 3.0;
    let sum: f64 = x + vec![y, z].iter().sum();
    println!("Sum is {sum}");
}

(Playground)

Error:

   Compiling playground v0.0.1 (/playground)
error[E0284]: type annotations needed
 --> src/main.rs:5:42
  |
5 |     let sum: f64 = x + vec![y, z].iter().sum();
  |                      -                   ^^^ cannot infer type of the type parameter `S` declared on the method `sum`
  |                      |
  |                      type must be known at this point
  |
  = note: cannot satisfy `<f64 as Add<_>>::Output == f64`
help: consider specifying the generic argument
  |
5 |     let sum: f64 = x + vec![y, z].iter().sum::<S>();
  |                                             +++++

For more information about this error, try `rustc --explain E0284`.
error: could not compile `playground` (bin "playground") due to previous error

Thanks!

Best, Oliver

You can make a type that implements Sum and also implement Add between it and f64. Then you'd have two different possible types for sum.

fn main() {
    let x: f64 = 1.0;
    let y: f64 = 2.0;
    let z: f64 = 3.0;
    let sum: f64 = x + vec![y, z].iter().sum::<A>();
    println!("Sum is {sum}");
}

struct A;

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

impl<'a> std::iter::Sum<&'a f64> for A {
    fn sum<I>(_iter: I) -> Self
    where
        I: Iterator<Item = &'a f64>,
    {
        A
    }
}

Rust Playground

4 Likes

You can easily conclude that the answer is "yes" if you look at the signature of Iterator::sum(). It's a generic, so it can be anything that impls Sum<f64>.

1 Like

Yes, in general, it can be anything. But in my code ...

But how could the compiler know that? Addition of impls is a non-breaking change (and the clairvoyance RFC is not yet implemented…).

If the compiler only took current impls into account, then:

  • downstream code would break your code via introducing ambiguity when a relevant impl is added;
  • and it would also violate coherence (again, additional impls would be potentially overlapping).

Thank you! Fascinating. I suppose a realistic example for such a type A would be complex numbers.

OK, so I understand it could be some other type in general. But no such type is in scope in my example, so it can only be f64, or can it? Is type inference being conservative here to prevent breaking in case such a type is added? Or would type inference become too costly if it inferred examples like this?

Thanks!

Yes, thank you, that makes sense.

A type doesn't need to be in scope to use it, just public. Rust doesn't care about things being in scope besides traits for trait methods.

I don't think cost is an issue since this only works if there's exactly one impl available.

1 Like