Why does 0 <= 0 panics here?

Hi all,
so I'm trying learning Rust from the Exercism track. In this exercise, I want to write a function that computes the "length" of a number, in the sense of powers of ten, i.e. the number of digits.
I need to work with BigInts, from crate num_bigint. Now, this code panics if I try to compute the length of BigInt::from(0):

pub fn length(test: &BigInt) -> BigInt {
    let mut res = BigInt::from(0);
    let mut temp = BigInt::from(1);
    while &temp <= test {
        dbg!(&temp);
        res += 1;
        temp *= 10;
    }
    res
}

In particular, seeing the outputs from the dbg!() call, it seems like the comparison &temp <= test causes some error. But why? Shouldn't it be just 0 <= 0? I've tested with other values, like BigInt::from(1), and it works. What am I doing wrong?Can I fix this?

Thanks!

I can't reproduce.

Best guess, you're not expecting a 0 return and are dividing by it or doing something else that can't work with 0. Special-case 0 to return 1 maybe.

1 Like

What is the actual panic message? Did you set RUST_BACKTRACE=1 as it suggests to see exactly what the call path was to that point?

Thanks to both, I'm adding a bit of context. The panic happens in this test:

use decimal::{Decimal, length};
use num_bigint::BigInt;
// custom
#[test]
fn length_single_digit() {
    assert!(length(&BigInt::from(1)) == BigInt::from(1));
    assert!(length(&BigInt::from(2)) == BigInt::from(1));
    assert!(length(&BigInt::from(5)) == BigInt::from(1));
    assert!(length(&BigInt::from(9)) == BigInt::from(1));
    assert!(length(&BigInt::from(0)) == BigInt::from(1));
}

Adding a second debug print:

pub fn length(test: &BigInt) -> BigInt {
    let mut res = BigInt::from(0);
    let mut temp = BigInt::from(1);
    dbg!(test);
    dbg!(&temp);
    while &temp <= test {
	dbg!(&temp);
	res += 1;
	temp *= 10;
    }
    res
}

Running with RUST_BACKTRACE=1, I got:

failures:

---- length_single_digit stdout ----
[src/lib.rs:22] test = 1
[src/lib.rs:23] &temp = 1
[src/lib.rs:25] &temp = 1
[src/lib.rs:22] test = 2
[src/lib.rs:23] &temp = 1
[src/lib.rs:25] &temp = 1
[src/lib.rs:22] test = 5
[src/lib.rs:23] &temp = 1
[src/lib.rs:25] &temp = 1
[src/lib.rs:22] test = 9
[src/lib.rs:23] &temp = 1
[src/lib.rs:25] &temp = 1
[src/lib.rs:22] test = 0
[src/lib.rs:23] &temp = 1
thread 'length_single_digit' panicked at 'assertion failed: length(&BigInt::from(0)) == BigInt::from(1)', tests/decimal.rs:25:5
stack backtrace:
   0: rust_begin_unwind
             at /rustc/90743e7298aca107ddaa0c202a4d3604e29bfeb6/library/std/src/panicking.rs:575:5
   1: core::panicking::panic_fmt
             at /rustc/90743e7298aca107ddaa0c202a4d3604e29bfeb6/library/core/src/panicking.rs:65:14
   2: core::panicking::panic
             at /rustc/90743e7298aca107ddaa0c202a4d3604e29bfeb6/library/core/src/panicking.rs:115:5
   3: decimal::length_single_digit
             at ./tests/decimal.rs:25:5
   4: decimal::length_single_digit::{{closure}}
             at ./tests/decimal.rs:20:1
   5: core::ops::function::FnOnce::call_once
             at /rustc/90743e7298aca107ddaa0c202a4d3604e29bfeb6/library/core/src/ops/function.rs:251:5
   6: core::ops::function::FnOnce::call_once
             at /rustc/90743e7298aca107ddaa0c202a4d3604e29bfeb6/library/core/src/ops/function.rs:251:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

Inspecting it with the debugger of VSCode, it seems like it's not the code that panics, but the test. Apparently, BigInts codifiy 0 as an empty vector, and not a vector containing 0. May this be the problem?

It has nothing to do with the internal representation of BigInts; the equivalent code for i32 will do the same thing:

#[test]
fn length_single_digit() {
    assert_eq!(length(1), 1);
    assert_eq!(length(2), 1);
    assert_eq!(length(5), 1);
    assert_eq!(length(9), 1);
    assert_eq!(length(0), 1);
}

pub fn length(test: i32) -> i32 {
    let mut res = 0;
    let mut temp = 1;
    while temp <= test {
	    res += 1;
	    temp *= 10;
    }
    res
}

Before the first loop iteration, temp is 1 and test is 0, so temp <= test is false and the loop never runs. You then return the initial value of res, which is zero.

Also, this code is incorrect for negative numbers: temp *= 10 will always make temp get smaller, and the loop will run until temp overflows.

Edit: I got things backwards in the struck-out text above. Negative inputs will always return zero here through the same path as a 0, and there will be no overflow in this case.

7 Likes

You're completely right, thanks! Now I feel a little dumby... :sweat_smile:

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.