Inner type conversion for iterator

I can't write iterator chain without type annotations.

pub fn sum(start: usize, end: usize, arr: &[i32]) -> i64{
        arr[start..=end].iter().sum()
    }

It does not work, because I need to sum into i64, and the trait Sum<&i32> is not implemented for i64.

I can do it explicitly: arr[start..=end].iter().map(|c| *c as i64).sum()

But, are there a way to write it with full type elision here? (Basically, I want to compiler to be smart enough to do it without manual type annotation/conversion based on function signature only).

I've tried:

  • arr[start..=end].iter().collect().sum()
  • arr[start..=end].iter().into().sum()

None is working.

Do you want into_iter() instead of iter()?

The problem is that i64 implements both Sum<i64> and Sum<&i64>, so you have to pick one when converting from &i32. When faced with multiple possibilities, the compiler won't try to guess further, even though only Sum<i64> can ultimately work here.

You can at least avoid the possibility of truncating-as by writing .map(|c| i64::from(*c)). If you don't want a closure, you can make that .copied().map(i64::from).

5 Likes

No, I want to write the expression without adding any type annotations (except for function signature).

I got the answer why it can't be done due to type ambiguity.

Thank you for explaining compiler reasoning.

For an alternative solution, I counted characters, |c| i64::from(*c) is 17 chars, and |c| *c as i64 is 13, so it's not trunking.

Oh, by "truncating-as" I meant protecting from a mistake where the input type is larger than the output type. For example, if you refactored to arr: &[i128] for some reason, there would be no sign of trouble when you convert as i64 here, but it would be losing the upper bits of data.

1 Like