4
\$\begingroup\$

I am getting started in Rust, and as a part of my first exercise, I decided to write a small program that converts a hexadecimal string to a binary string using a pattern matched lookup. I would appreciate a constructive review.

fn main() {
    let binary_value = convert_to_binary_from_hex("0x39A7F8");
    println!("Converted: {}", binary_value);   
}

fn convert_to_binary_from_hex(hex: &str) -> String {
   let to_binary = hex[2 ..]
       .chars()
       .map(|c| to_binary(c))
       .collect();

   to_binary
} 

fn to_binary(c: char) -> String {
    let b = match c {
       '0' => "0000",
       '1' => "0001",
       '2' => "0010",
       '3' => "0011",
       '4' => "0100",
       '5' => "0101",
       '6' => "0110",
       '7' => "0111",
       '8' => "1000",
       '9' => "1001",
       'A' => "1010",
       'B' => "1011",
       'C' => "1100",
       'D' => "1101",
       'E' => "1110",
       'F' => "1111",
        _  => "",
    };

   b.to_string()
}
\$\endgroup\$
  • \$\begingroup\$ To be clear, you are deliberately not using relevant tools from the standard library, right? \$\endgroup\$ – Shepmaster Sep 11 at 19:32
  • \$\begingroup\$ @Shepmaster - Correct. I am trying to familiarize myself with the primitives of the language before leveraging the standard library. \$\endgroup\$ – sc_ray Sep 11 at 19:34
2
\$\begingroup\$
  • Rustfmt is a tool for automatically formatting Rust code to the community-accepted style.
  • Clippy is a tool for finding common mistakes that may not be compilation errors but are unlikely to be what the programmer intended.

Rustfmt points out that you are using 3-space indents (Rust uses 4), and that some of your lines don't need to be split.

Clippy points out:

warning: returning the result of a let binding from a block
 --> src/main.rs:9:5
  |
7 |     let to_binary = hex[2..].chars().map(|c| to_binary(c)).collect();
  |     ----------------------------------------------------------------- unnecessary let binding
8 |
9 |     to_binary
  |     ^^^^^^^^^
  |
  = note: #[warn(clippy::let_and_return)] on by default
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#let_and_return
help: return the expression directly
  |
7 |
8 |
9 |     hex[2..].chars().map(|c| to_binary(c)).collect()
  |

warning: redundant closure found
 --> src/main.rs:7:42
  |
7 |     let to_binary = hex[2..].chars().map(|c| to_binary(c)).collect();
  |                                          ^^^^^^^^^^^^^^^^ help: remove closure as shown: `to_binary`
  |
  = note: #[warn(clippy::redundant_closure)] on by default
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_closure

There's no reason to return a String here, a &'static str is lighter-weight. See What are the differences between Rust's String and str? for full details, but a String requires a heap allocation, while a &'static str is a reference to some existing data in the compiled binary.

With these changes, all of your tests pass:

fn main() {
    let binary_value = convert_to_binary_from_hex("0x39A7F8");
    println!("Converted: {}", binary_value);
}

fn convert_to_binary_from_hex(hex: &str) -> String {
    hex[2..].chars().map(to_binary).collect()
}

fn to_binary(c: char) -> &'static str {
    match c {
        '0' => "0000",
        '1' => "0001",
        '2' => "0010",
        '3' => "0011",
        '4' => "0100",
        '5' => "0101",
        '6' => "0110",
        '7' => "0111",
        '8' => "1000",
        '9' => "1001",
        'A' => "1010",
        'B' => "1011",
        'C' => "1100",
        'D' => "1101",
        'E' => "1110",
        'F' => "1111",
        _ => "",
    }
}

In fact, deleting all of your code causes your tests to pass. It would be a good idea to add some tests!

Depending on what routes you want to look down next, you could try:

  • to handle strings that do not start with 0x without panicking.
  • to handle upper- and lower-case hex
  • to reduce/avoid string munging yourself
\$\endgroup\$
  • \$\begingroup\$ Thanks for the great feedback. I will definitely use rustfmt and clippy from now on.You mentioned that returning &'static str is lighter weight than returning String, Could you please elaborate on that in your response? \$\endgroup\$ – sc_ray Sep 11 at 21:34
  • 2
    \$\begingroup\$ @sc_ray: By returning a reference to the original string data (&str), you avoid an extra copy/allocation. The lifetime of the reference is 'static in this case because it is referencing data within the program's binary, not a runtime String. \$\endgroup\$ – Joe Clay Sep 12 at 15:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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