Как идиоматически конвертировать между u32 и usize?

Этот код работает и печатает "б":

fn main() {
    let s = "abc";
    let ch = s.chars().nth(1).unwrap();
    println!("{}", ch);
}

С другой стороны, этот код приводит к ошибке несоответствия типов.

fn main() {
    let s = "abc";
    let n: u32 = 1;
    let ch = s.chars().nth(n).unwrap();
    println!("{}", ch);
}
error[E0308]: mismatched types
 --> src/main.rs:5:28
  |
5 |     let ch = s.chars().nth(n).unwrap();
  |                            ^ expected usize, found u32

По какой-то внешней причине я должен использовать тип u32 для переменной n. Как я могу преобразовать u32 в usize и использовать его в nth()?

Ответ 1

Оператор as работает для всех типов номеров:

let ch = s.chars().nth(n as usize).unwrap();

Rust заставляет вас вводить целые числа, чтобы убедиться, что вы знаете о подписанности или переполнениях.

Целочисленные константы могут иметь суффикс типа:

let n = 1u32;

Однако обратите внимание, что отрицательные константы, такие как -1i32, являются внутренними - 1i32.

Целочисленные переменные, объявленные без явной спецификации типа, отображаются как {integer} и будут правильно выведены из одного из вызовов метода.

Ответ 2

Самое осторожное, что вы можете сделать, это использовать TryFrom и паниковать, когда значение не может поместиться в usize:

use std::convert::TryFrom;

fn main() {
    let s = "abc";
    let n: u32 = 1;
    let n_us = usize::try_from(n).unwrap();
    let ch = s.chars().nth(n_us).unwrap();
    println!("{}", ch);
}

При слепом использовании as ваш код будет таинственным образом не работать при запуске на платформе, где usize меньше 32-битного. Например, некоторые микроконтроллеры используют 16-битные целые числа в качестве собственного размера:

fn main() {
    let n: u32 = 0x1_FF_FF;
    // Pretend that 'usize' is 16-bit
    let n_us: u16 = n as u16;

    println!("{}, {}", n, n_us); // 131071, 65535
}

Ответ 3

Теперь у нас совсем другой ответ, когда мы пытаемся скомпилировать ваш код, заменив число 1 на переменную типа i32:

error[E0308]: mismatched types
 --> src/main.rs:5:28
  |
5 |     let ch = s.chars().nth(n).unwrap();
  |                            ^ expected usize, found i32
help: you can convert an 'i32' to 'usize' and panic if the converted value wouldn't fit
  |
5 |     let ch = s.chars().nth(n.try_into().unwrap()).unwrap();
  |    

Это означает, что теперь компилятор рекомендует использовать n.try_into().unwrap(), который использует черту TryInto, которая в свою очередь опирается на TryFrom и возвращает Result<T, T::Error>. Вот почему нам нужно извлечь результат с помощью .unwrap()

TryInto документация