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?
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.