2
\$\begingroup\$

Problem statement: Write a program that takes two command-line arguments number and k. The program should convert number to base k. Assume the base is between 2 and 16 inclusive. For bases greater than 10, use the letters A through F to represent the digits 10 through 15, respectively.

This is one of my self-imposed challenges in Rust to become better at it. The problem was taken from Sedgewick Exercise 1.3.21.

Here is my code:

use std::env;

fn main() {
    let arguments: Vec<String> = env::args().collect();
    let mut number: u32 = arguments[1].parse().unwrap();
    let k: u32 = arguments[2].parse().unwrap();
    
    let number_original: u32 = number;
    let mut largest_power: u32 = 1;
    let mut kary: String = String::new();

    while largest_power <= number/k {
        largest_power *= k;
    }

    while largest_power > 0 {
        if largest_power > number {
            kary.push_str(&format!("{}", "0"));
        } else {
            let dividend: u32 = number/largest_power;

            if number/largest_power < 10 {
                kary.push_str(&format!("{}", dividend.to_string())); 
                number -= dividend*largest_power;
            }

            else if number/largest_power == 10 {kary.push_str(&format!("{}", "A")); number -= dividend*largest_power;}
            else if number/largest_power == 11 {kary.push_str(&format!("{}", "B")); number -= dividend*largest_power;}
            else if number/largest_power == 12 {kary.push_str(&format!("{}", "C")); number -= dividend*largest_power;}
            else if number/largest_power == 13 {kary.push_str(&format!("{}", "D")); number -= dividend*largest_power;}
            else if number/largest_power == 14 {kary.push_str(&format!("{}", "E")); number -= dividend*largest_power;}
            else if number/largest_power == 15 {kary.push_str(&format!("{}", "F")); number -= dividend*largest_power;}
        }

        largest_power /= k;
    }

    match k {
        2..=16 => println!("The number {} at base {} is represented with {}.", number_original, k, kary),
        _ => panic!("Base must be between 2 and 16."),
    }
}

Is there any way that I can improve my code?

\$\endgroup\$

1 Answer 1

1
\$\begingroup\$

Don't unwrap

Panicking is not a good way to handle errors. Rather provide an error message in case of an Err and exit the program gracefully.

Use libraries

If you'd use e.g. clap you can let it parse the CLI arguments for you as the desired data type (u32 in your case) and gracefully handle the respective errors.
There's also radix_fmt available to do the conversion to any base.

Use expressive variable names

The name kary is misleading. It sounds like a person's name. If you're referring to k-ary numbers, maybe rename it to k_ary. Though a less technical term such as digits should also suffice and might be more widely understood.

Use rustfmt or cargo fmt

... to format your code with a standardized style.

Validate your input.

Running the program e.g. with a base 1 results in an infinite loop. Invalid input as base-1 should be caught early. Similarly base-0 inputs panic the program.

Don't panic!()

If you encounter an error use e.g. eprintln!() to print an error message and use std::process::exit() to exit the program with a possibly nonzero return code.

Suggested:

src/main.rs

use clap::Parser;
use radix_fmt::radix;
use std::ops::RangeInclusive;
use std::process::exit;

const VALID_BASES: RangeInclusive<u8> = 2..=36;

#[derive(Debug, Parser)]
struct Args {
    #[arg(index = 1)]
    number: u32,
    #[arg(index = 2)]
    base: u8,
}

fn main() {
    let args = Args::parse();

    if !VALID_BASES.contains(&args.base) {
        eprintln!(
            "Base must be in {}..={}",
            VALID_BASES.start(),
            VALID_BASES.end()
        );
        exit(1);
    }

    println!(
        "Number {} in base {} is: {}",
        args.number,
        args.base,
        radix(args.number, args.base)
    );
}

Cargo.toml

[package]
name = "bases"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
clap = { version = "4.3.11", features = ["derive"] }
radix_fmt = "1.0.0"
\$\endgroup\$
1
  • 2
    \$\begingroup\$ I would not print the debug format of the range. Debug format is intended for programmers, and it may change without warning. It is better to use a custom printing. \$\endgroup\$ Jul 9, 2023 at 9:49

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.