Avatar ·

Как преобразовать символ char в число int в Rust, чтобы '1' стало 1?

Всем привет! Я только начал изучать Rust и столкнулся с простой, на первый взгляд, задачей. Мне нужно прочитать символ из строки и преобразовать его в целое число. Например, у меня есть строка "123", и я хочу пройтись по ней циклом, взять каждый символ ('1', '2', '3') и превратить его в соответствующее целое число (1, 2, 3). В других языках, например в Python или JavaScript, это делается очень просто: `int('1')` или `parseInt('1')`. Но в Rust я не могу найти прямого аналога. Я попробовал написать такой код: ```rust fn main() { let c: char = '1'; let n: i32 = c as i32; // попытка 1 println!("{}", n); } ``` Но это выводит 49, а не 1. Я понимаю, что 49 — это ASCII-код символа '1'. Как правильно получить именно числовое значение цифры, а не её код? Мне нужно решение, которое будет работать с любыми цифрами от '0' до '9'. Желательно, чтобы оно было идиоматичным для Rust и не содержало unsafe кода. Заранее спасибо за помощь! <<>>

Правильное преобразование char в число в Rust

Вы столкнулись с типичной ловушкой для новичков в Rust. Прямое приведение типа char as i32 даёт ASCII-код символа, а не его числовое значение. Для корректного преобразования цифрового символа в целое число в Rust есть несколько способов, и самый правильный — использовать метод to_digit().

Основной способ: метод to_digit()

Метод to_digit(radix) определён для типа char. Он принимает основание системы счисления (radix) от 2 до 36 и возвращает Option<u32>. Для десятичных цифр используйте основание 10:

fn main() {
    let c: char = '1';
    let n: u32 = c.to_digit(10).expect("Это не цифра!");
    println!("{}", n); // Выведет: 1
}

Почему это правильно? Потому что:

  • Метод работает не только с '0'-'9', но и с буквами для систем счисления выше 10 (например, 'a' → 10).
  • Возвращает Option, что заставляет вас обрабатывать случай, когда символ не является цифрой.
  • Идиоматично для Rust — использует систему типов для безопасности.

Обработка ошибок

Если вы не уверены, что символ точно цифра, лучше не использовать expect(), а обработать None:

fn main() {
    let c: char = 'x';
    match c.to_digit(10) {
        Some(n) => println!("Число: {}", n),
        None => println!("'{}' не является цифрой", c),
    }
}

Преобразование в другие целочисленные типы

Метод to_digit() возвращает u32. Если вам нужно i32 или другой тип, используйте as или From/TryFrom:

let c: char = '5';
let n_u32 = c.to_digit(10).unwrap(); // u32
let n_i32 = n_u32 as i32; // безопасно, т.к. 0-9 помещается в i32
let n_i64: i64 = n_u32.into(); // From для i64

// Альтернатива: сразу в i32 через map
let n_i32: i32 = c.to_digit(10)
    .map(|d| d as i32)
    .expect("Не цифра");
println!("{}", n_i32); // 5

Обработка строки целиком

Если вам нужно преобразовать всю строку цифр в число, не делайте это посимвольно. Используйте str::parse():

fn main() {
    let s = "123";
    let n: i32 = s.parse().expect("Не число");
    println!("{}", n); // 123
}

Это работает для целых чисел, чисел с плавающей точкой и других типов, реализующих FromStr.

Старый способ (не рекомендуется)

Раньше иногда использовали вычитание кода символа '0':

let c: char = '7';
let n = c as u32 - '0' as u32; // 7

Это работает, но менее безопасно: если c не цифра, вы получите мусор. Всегда используйте to_digit() — это современный, безопасный и идиоматичный способ.

Итог

Для преобразования отдельного символа-цифры в число в Rust используйте char::to_digit(10). Он возвращает Option<u32>, что заставляет вас корректно обрабатывать ошибки. Для строк из нескольких цифр применяйте str::parse().

<<>>

Альтернативный подход: использование match для явного отображения

Если вы работаете только с десятичными цифрами и хотите максимально явного контроля, можно использовать сопоставление с образцом:

fn char_to_digit(c: char) -> Option<u32> {
    match c {
        '0' => Some(0),
        '1' => Some(1),
        '2' => Some(2),
        '3' => Some(3),
        '4' => Some(4),
        '5' => Some(5),
        '6' => Some(6),
        '7' => Some(7),
        '8' => Some(8),
        '9' => Some(9),
        _ => None,
    }
}

fn main() {
    let c = '4';
    if let Some(n) = char_to_digit(c) {
        println!("{}", n); // 4
    }
}

Этот код полностью эквивалентен to_digit(10), но более многословен. Его преимущество — наглядность для новичков и возможность добавить специфичную логику (например, логирование или дополнительные проверки).

Однако для production-кода лучше использовать встроенный to_digit(), так как он короче, оптимизирован компилятором и не содержит риска опечаток. Ручной match оправдан только в учебных целях или если вам нужно обрабатывать нестандартные символы (например, арабские цифры или римские числа).

Также помните, что для преобразования строки целиком всегда предпочтительнее parse(), чем посимвольная обработка — это и быстрее, и безопаснее.