Search code examples
rustencodingutf-8

How to convert a u8 to a char in Rust?


I have a function that builds a string based off u8 values in an array. If the u8 is 0 or 10, then I push a specific character. Otherwise, I want to push on the number itself. My issue is that when I print the result of this function, I get the numbers escaped. Here is the code:

fn convert_result_to_string(guess_result: &[u8; SEQ_LEN]) -> String {
    let mut output = String::new();

    for (i, num) in guess_result.iter().enumerate() {

        if *num == 0 {
            output.push_str("*");
        }
        else if *num == 10 {
            output.push_str("$");
        }
        else {
            output.push(*num as char);
        }
        if i < SEQ_LEN-1 {
            output.push(' ');
        }
    }

    output
}

Then later on when I call the

let test = [0, 5, 6, 10, 7];
let o = convert_result_to_string(&test);
println!("o: {:?}", o);

I get the following output:

o: "* \u{5} \u{6} $ \u{7}"

Why are the 5, 6, and 7 escaped?


Solution

  • You cannot just cast a u8 as a char and expect the result to display a digit. The digits 0-9 are represented by the ASCII (and Unicode) codepoints 48-57. ASCII codepoints 0-9 consist of the null character, whitespace, and control characters not widely used anymore.

    These characters get escaped because you are using debug formatting ({:?}) to display the string, and ASCII codepoints 5, 6, and 7 are typically rendered as placeholder boxes or are invisible, depending on the font or terminal emulator in use. Therefore, when formatting using debug formatting, Rust shows you what you would have to paste into Rust source code to obtain an equivalent string, in human-readable form that isn't dependent on how one's font or terminal emulator represents these characters. This uses Rust's Unicode escape sequence \u to embed these inconsistently-displayable characters in the string.

    For your use case, you are probably looking for char::from_digit().

    output.push(char::from_digit((*num).into(), 10).expect("digit must be in the range 0-9"));
    

    (Playground)


    Note the above code will panic if the value of *num is not between 0 and 9 (inclusive). Consider doing something like this, which will also handle multi-digit decimal numbers:

    write!(output, "{num}").unwrap();
    

    It's also much shorter and cannot fail. (String's implementation of std::fmt::Write is infallible.)

    Note that this requires you to use std::fmt::Write; in your module/function.

    (Playground)