How do I convert between numeric types safely and idiomatically?
Converting values
From a type that fits completely within another
There's no problem here. Use the From
trait to be explicit that there's no loss occurring:
fn example(v: i8) -> i32 {
i32::from(v) // or v.into()
}
You could choose to use as
, but it's recommended to avoid it when you don't need it (see below):
fn example(v: i8) -> i32 {
v as i32
}
From a type that doesn't fit completely in another
There isn't a single method that makes general sense - you are asking how to fit two things in a space meant for one. One good initial attempt is to use an Option
— Some
when the value fits and None
otherwise. You can then fail your program or substitute a default value, depending on your needs.
Since Rust 1.34, you can use TryFrom
:
use std::convert::TryFrom;
fn example(v: i32) -> Option<i8> {
i8::try_from(v).ok()
}
Before that, you'd have to write similar code yourself:
fn example(v: i32) -> Option<i8> {
if v > std::i8::MAX as i32 {
None
} else {
Some(v as i8)
}
}
From a type that may or may not fit completely within another
The range of numbers isize
/ usize
can represent changes based on the platform you are compiling for. You'll need to use TryFrom
regardless of your current platform.
See also:
- How do I convert a usize to a u32 using TryFrom?
- Why is type conversion from u64 to usize allowed using `as` but not `From`?
What as
does
but
4294967296us as u32
will silently overflow and give a result of 0
When converting to a smaller type, as
just takes the lower bits of the number, disregarding the upper bits, including the sign:
fn main() {
let a: u16 = 0x1234;
let b: u8 = a as u8;
println!("0x{:04x}, 0x{:02x}", a, b); // 0x1234, 0x34
let a: i16 = -257;
let b: u8 = a as u8;
println!("0x{:02x}, 0x{:02x}", a, b); // 0xfeff, 0xff
}
See also:
About ToPrimitive
/ FromPrimitive
Ideally [...]
ToPrimitive
[...] would all be removed in favor of a more principled way of working with C-like enums
In the meantime, these traits live on in the num crate:
Related videos on Youtube
Comments
-
Caspar about 2 years
Editor's note: This question is from a version of Rust prior to 1.0 and references some items that are not present in Rust 1.0. The answers still contain valuable information.
What's the idiomatic way to convert from (say) a
usize
to au32
?For example, casting using
4294967295us as u32
works and the Rust 0.12 reference docs on type casting sayA numeric value can be cast to any numeric type. A raw pointer value can be cast to or from any integral type or raw pointer type. Any other cast is unsupported and will fail to compile.
but
4294967296us as u32
will silently overflow and give a result of 0.I found
ToPrimitive
andFromPrimitive
which provide nice functions liketo_u32() -> Option<u32>
, but they're marked as unstable:#[unstable(feature = "core", reason = "trait is likely to be removed")]
What's the idiomatic (and safe) way to convert between numeric (and pointer) types?
The platform-dependent size of
isize
/usize
is one reason why I'm asking this question - the original scenario was I wanted to convert fromu32
tousize
so I could represent a tree in aVec<u32>
(e.g.let t = Vec![0u32, 0u32, 1u32]
, then to get the grand-parent of node 2 would bet[t[2us] as usize]
), and I wondered how it would fail ifusize
was less than 32 bits.-
Shepmaster over 9 yearsBe careful using
isize
/usize
- the range of numbers they can represent changes based on the platform you are compiling for! Your example might be better expressed usingu64
instead.
-
-
Caspar over 9 yearsRe how to fit 2 things in a space meant for 1: exactly, hence an API that returns an
Option<u32>
seems like exactly what I want - is there no replacement forToPrimitive
? That's the type of answer I was expecting (though your snippet is also useful). -
Shepmaster over 9 years@Caspar I can't point to a straight replacement, but did amend my answer to include some more pointers about what to do.
-
phimuemue over 7 years@Caspar I needed the same and packaged into an own crate: crates.io/crates/as_num
-
Hutch Moore almost 4 yearsI thought
as
panics in debug mode, and only silently overflows in release mode? -
Shepmaster almost 4 years@HutchMoore
as
never panics, AFAIK. Only mathematical operations (e.g. addition, subtraction) panic. While usually thought of as debug/release, it's separately controllable. See How to compile and run an optimized Rust program with overflow checking enabled -
sam over 3 yearsExample of where using as can go wrong...
println!("{}", (-1i32) as u32)
will output4294967196
with no error -
Vít Skalický about 2 yearsHow do I convert usize and isize into u64? From does not seem to work for me or am I just dumb?
-
Shepmaster about 2 years@VítSkalický How to idiomatically convert between u32 and usize?.
From
/Into
don't work becauseusize
changes size depending on what platform you are building for (Why is type conversion from u64 to usize allowed usingas
but notFrom
?).