70

This code works and prints "b":

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

On the other hand, this code results in a mismatch type error.

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

For some external reason, I have to use the u32 type for variable n. How can I convert u32 to usize and use it in nth()?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Sajuuk
  • 2,667
  • 3
  • 22
  • 34

3 Answers3

68

The most cautious thing you can do is to use TryFrom and panic when the value cannot fit within a 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);
}

By blindly using as, your code will fail in mysterious ways when run on a platform where usize is smaller than 32-bits. For example, some microcontrollers use 16-bit integers as the native size:

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
}

For broader types of numeric conversion beyond u32 <-> usize, refer to How do I convert between numeric types safely and idiomatically?.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • I think before using `TryFrom`, FOR SOME CASES, a developer could also use the `From` trait (e.g. `u32::from(n)`. This should issue an error at compile-time if the cast is not implemented in the Rust-Compiler. HOWEVER: As far as i can see, the `From` trait for `isize` and `usize` is only implemented for u8 and u16 but are not checked depending on the platform (see https://doc.rust-lang.org/src/core/convert/num.rs.html#44 and https://doc.rust-lang.org/src/core/convert/num.rs.html#135). This could be an issue for 8-bit microcontrollers (not sure if Rust supports them) – obraunsdorf Apr 21 '21 at 14:52
  • @obraunsdorf linked to an existing answer that covers that, thanks. – Shepmaster Apr 22 '21 at 17:44
58

The as operator works for all number types:

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

Rust forces you to cast integers to make sure you're aware of signedness or overflows.

Integer constants can have a type suffix:

let n = 1u32;

However, note that negative constants, such as -1i32 is internally - 1i32.

Integer variables declared without an explicit type specification are shown as {integer} and will be properly inferred from one of the method calls.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Tatsuyuki Ishi
  • 3,883
  • 3
  • 29
  • 41
  • 9
    To protect people who blindly copy&paste this answer: I think the answer from @Shepmaster (https://stackoverflow.com/a/55769098/1290383) should be the "accepted" answer. Someone might stumble across this answer here thinking that using `as` is save for numeric casts but it's not. – obraunsdorf Apr 21 '21 at 14:11
  • casts between integertypes are safe in the sense they produve deterministic results. Tgose results may not be what you want, but the same applies to nost numeric operations. – plugwash Jun 15 '22 at 20:08
2

We now have a pretty different answer when we try to compile your code, replacing the number 1 with a variable of type 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();
  |    

It means that now the compiler recommends you to use n.try_into().unwrap() that makes use of the trait TryInto which in turn relies on TryFrom and returns a Result<T, T::Error>. That's why we need to extract the result with a .unwrap()

TryInto documentation

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Alexandro de Oliveira
  • 1,291
  • 17
  • 16
  • 1
    This is effectively already covered by an [existing answer](https://stackoverflow.com/a/55769098/155423), as `TryFrom` / `TryInto` are mirrors of each other. – Shepmaster Sep 30 '19 at 13:02