72

I am trying to find the sum of the digits of a given number. For example, 134 will give 8.

My plan is to convert the number into a string using .to_string() and then use .chars() to iterate over the digits as characters. Then I want to convert every char in the iteration into an integer and add it to a variable. I want to get the final value of this variable.

I tried using the code below to convert a char into an integer:

fn main() {
    let x = "123";
    for y in x.chars() {
        let z = y.parse::<i32>().unwrap();
        println!("{}", z + 1);
    }
}

(Playground)

But it results in this error:

error[E0599]: no method named `parse` found for type `char` in the current scope
 --> src/main.rs:4:19
  |
4 |         let z = y.parse::<i32>().unwrap();
  |                   ^^^^^

This code does exactly what I want to do, but first I have to convert each char into a string and then into an integer to then increment sum by z.

fn main() {
    let mut sum = 0;
    let x = 123;
    let x = x.to_string();
    for y in x.chars() {
        // converting `y` to string and then to integer
        let z = (y.to_string()).parse::<i32>().unwrap();
        // incrementing `sum` by `z`
        sum += z;
    }
    println!("{}", sum);
}

(Playground)

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
ritiek
  • 2,477
  • 2
  • 18
  • 25
  • It gives me `binary operation + cannot be applied to type char` when I try to add something to it – ritiek May 15 '17 at 15:39
  • If you want to parse the whole integer, all you need is `let v: i32 = x.parse()?;`. Can you check whether this is not a duplicate of http://stackoverflow.com/questions/27043268/convert-a-string-to-int-in-rust ? – E_net4 May 15 '17 at 15:40
  • 2
    Sorry, that may have been confusing. It's an integral value, but it's a separate type because it's not a type you are supposed to do arithmetic on. My question stands: what output do you expect from your program? – Matthieu M. May 15 '17 at 15:40
  • @Ritiek: No problem; I also have the same problem when asking a question that the context seems obvious to me since I've been working on it for some time :) – Matthieu M. May 15 '17 at 18:16
  • @shepmaster: Isn't the `+1`confusing as the code would print `2,3,4` (if it would compile). – Christopher Oezbek Jun 18 '19 at 20:45
  • @ChristopherOezbek perhaps, but it's *what the OP tried*. Presumably they added that to demonstrate that they wanted the ability to use addition on the result, but that's just guessing on my part. – Shepmaster Jun 18 '19 at 20:49

4 Answers4

95

The method you need is char::to_digit. It converts char to a number it represents in the given radix.

You can also use Iterator::sum to calculate sum of a sequence conveniently:

fn main() {
    const RADIX: u32 = 10;
    let x = "134";
    println!("{}", x.chars().map(|c| c.to_digit(RADIX).unwrap()).sum::<u32>());
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Pavel Strakhov
  • 39,123
  • 5
  • 88
  • 127
  • 1
    `flat_map` may be used if you don't care about the failure to convert. – Shepmaster May 15 '17 at 18:17
  • @AurevoirXavier nope I did not mean that. – Shepmaster Nov 27 '17 at 13:10
  • @Shepmaster do you mean like `x.chars().flat_map(|x| c.to_digit(RADIX))`? Is this because flattening a result will either panic or give the value? – aravk33 Dec 04 '19 at 11:34
  • @aravk33, [`filter_map`](https://doc.rust-lang.org/std/iter/trait.Iterator.html#method.filter_map) is a good fit here since it will uwrap `Option`: `x.chars().filter_map(|c| c.to_digit(RADIX))`. I'll update the answer. – murla Dec 08 '19 at 16:25
  • 1
    @murlakatamenka No, I mean `flat_map`. See [Why does `Option` support `IntoIterator`?](https://stackoverflow.com/q/43285372/155423) – Shepmaster Dec 09 '19 at 14:46
  • @aravk33 no it does not panic. See [Why does `Option` support `IntoIterator`?](https://stackoverflow.com/q/43285372/155423) – Shepmaster Dec 09 '19 at 14:46
20
my_char as u32 - '0' as u32

Now, there's a lot more to unpack about this answer.

It works because the ASCII (and thus UTF-8) encodings have the Arabic numerals 0-9 ordered in ascending order. You can get the scalar values and subtract them.

However, what should it do for values outside this range? What happens if you provide 'p'? It returns 64. What about '.'? This will panic. And '♥' will return 9781.

Strings are not just bags of bytes. They are UTF-8 encoded and you cannot just ignore that fact. Every char can hold any Unicode scalar value.

That's why strings are the wrong abstraction for the problem.

From an efficiency perspective, allocating a string seems inefficient. Rosetta Code has an example of using an iterator which only does numeric operations:

struct DigitIter(usize, usize);

impl Iterator for DigitIter {
    type Item = usize;
    fn next(&mut self) -> Option<Self::Item> {
        if self.0 == 0 {
            None
        } else {
            let ret = self.0 % self.1;
            self.0 /= self.1;
            Some(ret)
        }
    }
}

fn main() {
    println!("{}", DigitIter(1234, 10).sum::<usize>());
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
5

If c is your character you can just write:

c as i32 - 0x30;

Test with:

let c:char = '2';
let n:i32 = c as i32 - 0x30;
println!("{}", n);

output:

2

NB: 0x30 is '0' in ASCII table, easy enough to remember!

Antonin GAVREL
  • 9,682
  • 8
  • 54
  • 81
4

Another way is to iterate over the characters of your string and convert and add them using fold.

fn sum_of_string(s: &str) -> u32 {
  s.chars().fold(0, |acc, c| c.to_digit(10).unwrap_or(0) + acc)
}

fn main() {
    let x = "123";
    println!("{}", sum_of_string(x));
}
manonthemat
  • 6,101
  • 1
  • 24
  • 49