snippetrustCritical
How do I convert between numeric types safely and idiomatically?
Viewed 0 times
safelyhowidiomaticallynumericandbetweenconverttypes
Problem
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
For example, casting using
A 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
I found
What's the idiomatic (and safe) way to convert between numeric (and pointer) types?
The platform-dependent size of
What's the idiomatic way to convert from (say) a
usize to a u32?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 and FromPrimitive which provide nice functions like to_u32() -> Option, 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 from u32 to usize so I could represent a tree in a Vec (e.g. let t = Vec![0u32, 0u32, 1u32], then to get the grand-parent of node 2 would be t[t[2us] as usize]), and I wondered how it would fail if usize was less than 32 bits.Solution
Converting values
From a type that fits completely within another
There's no problem here. Use the
You could choose to use
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
Since Rust 1.34, you can use
Before that, you'd have to write similar code yourself:
From a type that may or may not fit completely within another
The range of numbers
See also:
What
but
When converting to a smaller type,
See also:
About
RFC 369, Num Reform, states:
Ideally [...]
In the meantime, these traits live on in the num crate:
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::try_from(v).ok()
}Before that, you'd have to write similar code yourself:
fn example(v: i32) -> Option {
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
asbut notFrom?
What
as doesbut
4294967296us as u32 will silently overflow and give a result of 0When 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:
- What is the difference between From::from and as in Rust?
About
ToPrimitive / FromPrimitiveRFC 369, Num Reform, states:
Ideally [...]
ToPrimitive [...] would all be removed in favor of a more principled way of working with C-like enumsIn the meantime, these traits live on in the num crate:
ToPrimitive
FromPrimitive
Code Snippets
fn example(v: i8) -> i32 {
i32::from(v) // or v.into()
}fn example(v: i8) -> i32 {
v as i32
}use std::convert::TryFrom;
fn example(v: i32) -> Option<i8> {
i8::try_from(v).ok()
}fn example(v: i32) -> Option<i8> {
if v > std::i8::MAX as i32 {
None
} else {
Some(v as i8)
}
}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
}Context
Stack Overflow Q#28273169, score: 234
Revisions (0)
No revisions yet.