Converting 0, 1, 2, .., 9 to '0', '1', .., '9'

let x: i32, be a value >=0 and <= 9

I want to convert ‘x’ to a char ‘c’

If this was plain C, we could abuse all types of things and do:

char c = (char) ('0' + x)

With this being rust, what is the best way to do this conversion?

Basically I need the inverse of https://doc.rust-lang.org/std/primitive.char.html#method.to_digit

EDIT: @mbrubeck’s solution is better, you should rather use that one.

let c: char = match x {
    | 0 ..= 9 => ('0' as u8 + x as u8).into(),
    | _ => unimplemented!("Handle other case"),
};
1 Like

See also std::char::from_digit.

9 Likes

Sorry for derailing the conversation abit here, but I’m just curious as to why I’ve seen this syntax many times and have never read about it:

match foo() {
    | 0..128 => println!("7 bit number!"),
//  ^ This 
    | 128..256 => println!("8 bit number!")
//  ^ And this
}

I’m just curious as to how it’s meant to be interpreted, or if it’s just a visual guide.

3 Likes

I think it’s the same sort of thing as allowing trailing commas when passing arguments to a function or in a struct definition. When you’ve got several branches with different patterns allowing that leading | probably leads to “nicer” looking code and less noisy diffs, while still being unambiguous to rustc's parser.

1 Like

Yep, that’s it, @Michael-F-Bryan guessed correctly.

The first reason is that I was used to leading pipes in enum arms to begin with (OCaml style).

The reason to replicate the style in Rust (after discovering that it was valid Rust :)), is that I like to keep the coding style consistent no matter how big the code block is, and I have found that for huge enum matches, having the initial pipe makes the match more readable / easier to spot within the code, imho (patterns being between a pipe | and the arrow =>):

use ::core::mem::drop as do_stuff_with;

enum Kind { A, B, C }

struct BigStruct {
    kind: Kind,
    foo: i32,
    bar: Inner,
}

enum Inner {
    Id(u64),
    Named(String),
}

fn example (x: &'_ BigStruct)
{
    match *x {
        | BigStruct {
            kind: Kind::A,
            foo,
            bar: Inner::Named(ref name),
        },
        | BigStruct {
            kind: Kind::B,
            foo,
            bar: Inner::Named(ref name),
        },
        => {
            do_stuff_with::<i32>(foo);
            let new_name: String =
                name.chars()
                    .map(|c| match c {
                        | '-' => '_',
                        |  _  => c,
                    })
                    .collect()
            ;
            do_stuff_with(new_name);
        },

        | _ => {
            unimplemented!("This is just an example");
        },
    }
}
2 Likes

Your example just made me realize that this

match name {
    | "Jan" | "Piet" | "Koos" => do_something(),
    | _ => unimplemented!("No match!"),
}

is like switch fallthrough in C/C++.

switch name {
    case "Jan":
    case "Piet":
    case "Koos": {
        do_something();
        break;
    }
    default:
        std::cout << "No match!";
}

The difference being, in C/C++ you have to remember to opt out (more error-prone), while in Rust you have to opt in (less error-prone). Yay for more safety! :wink:

3 Likes