Armstrong Numbers

Hi, I would like some advice about my code for solving the Armstrong Numbers exercise.

The problem is quite simple, Armstrong Numbers are numbers equal to the sum the digits to the power of the length. For example: 153 == 1^3 + 5^3 + 3^3. Here the length is 3. (They consider decimal system only.)

Is there an option that is obviously better in the two cases below?

Reviews or comments not using very advanced stuff (mostly unsafe, memory tricks and so on) would be welcome.

I can't be sure these are totally correct, but they pass all tests.

Both panic on is_armstrong_number(3_999_999_999u32)) because of overflows.

String version is easier to patch:

        ...
        .try_fold(num, |acc, x| acc.checked_sub(
                                    (x as u32).pow(lenght)
                                ));
    out == Some(0)
}

But in theory it shouldn't panic right?

3**10+9**10 = 3486843450
2**32-1 =     4294967295

I may be confused, quite tired rn.

Oh, it is 3**10+9**11 = 31381118658 and does overflow.

you can save the allocation and string conversion even tho it's a bit less elegant looking

//! A power number such as:
//! 153 = 1^3 + 5 ^3 + 3^3 = 153
//! i.e. sum each digit elevated to the power of its length.
// Base 10 only at the moment.
pub fn is_armstrong_number(num: u32) -> bool {
    let lenght = num.checked_ilog10().unwrap_or(0) + 1
    let mut remaining_digits=num;
    let mut remaining_total=num;
    
    for i in 0..lenght{
        let digit=remaining_digits%10;
        remaining_digits=remaining_digits/10;
        let power=digit.pow(lenght);
        if power>remaining_total{
            return false;
        }
        remaining_total-=power;
    }
    return true;
}

I'm sort of scared of this error case (outside of this little exercise).

It was hard to see (for me), and may be missed on testing, unless one is very careful.

I do take away that extreme values, 0, and other cases should always be tested.

Running it exhaustively for all u32 would seem exaggerated (even after development is nearly done), and would be harder with a few more parameters.

Note also: 3_999_999_999u32 is not an extreme value of u32, only of your logic.

no_panic - Rust could be a solution (I'd still prefer if it was provided by compiler...)

I've refactored using a mix of both suggestions. It may still be non-optimal.

pub fn is_armstrong_number(num: u32) -> bool {
    if num == 0 {
        return true;
    }

    let l = num.ilog10() + 1; // length

    let mut d = 0; // The current digit
    let mut num_upd = num; // Update number elsewhere.

    let result: Option<u32> = (0..l).try_fold(0u32, |acc, _| {
        d = num_upd % 10;
        num_upd /= 10;
        acc.checked_add(d.pow(l))
    });

    result == Some(num)
}

PS: I think the function signature given by the exercise should return Result<bool, String> or Option<bool>.

I wonder whether others would agree. Maybe not for this exercise but in other cases.