How to idiomatically convert between u32 and usize?
Solution 1
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.
Solution 2
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:
Solution 3
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()
Related videos on Youtube
Comments
-
Sajuuk about 2 years
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 variablen
. How can I convertu32
tousize
and use it innth()
?-
Kevin Wang almost 4 yearsWhat are some common use cases for converting
usize
tou32
, and vice versa?
-
-
Shepmaster over 4 yearsThis is effectively already covered by an existing answer, as
TryFrom
/TryInto
are mirrors of each other. -
obraunsdorf about 3 yearsTo protect people who blindly copy&paste this answer: I think the answer from @Shepmaster (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 about 3 yearsI think before using
TryFrom
, FOR SOME CASES, a developer could also use theFrom
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, theFrom
trait forisize
andusize
is only implemented for u8 and u16 but are not checked depending on the platform (see doc.rust-lang.org/src/core/convert/num.rs.html#44 and 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) -
Shepmaster about 3 years@obraunsdorf linked to an existing answer that covers that, thanks.