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 a u32?
For example, casting using 4294967295us as u32 works and the Rust 0.12 reference docs on type casting say
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 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<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 from u32 to usize so I could represent a tree in a Vec<u32> (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.
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:
What is the difference between From::from and as in Rust?
About ToPrimitive / FromPrimitive
RFC 369, Num Reform, states:
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:
ToPrimitive
FromPrimitive
Related
I have a simple struct that I would like to implement Index for, but as a newcomer to Rust I'm having a number of troubles with the borrow checker. My struct is pretty simple, I'd like to have it store a start and step value, then when indexed by a usize it should return start + idx * step:
pub struct MyStruct {
pub start: f64,
pub step: f64,
}
My intuition is that I'd simply be able to take the signature of Index and plug in my types:
impl Index<usize> for MyStruct {
type Output = f64;
fn index(&self, idx: usize) -> &f64 {
self.start + (idx as f64) * self.step
}
}
This gives the error mismatched types saying expected type &f64, found type f64. As someone who has yet to fully understand how Rust's type system works, I tried simply slapping & on the expression:
fn index(&self, idx: usize) -> &f64 {
&(self.start + (idx as f64) * self.step)
}
This now tells me that the borrowed value does not live long enough, so maybe it needs a lifetime variable?
fn index<'a>(&self, idx: usize) -> &'a f64 {
&(self.start + (idx as f64) * self.step)
}
The error is the same, but the note now gives lifetime 'a instead of lifetime #1, so I guess that's not necessary, but at this point I feel like I'm stuck. I'm confused that such a simple exercise for most languages has become so difficult to implement in Rust, since all I want to do is return a computation from a function that happens to be behind a reference. How should I go about implementing Index for a simple structure where the value is calculated on demand?
The Index trait is meant to return a borrowed pointer to a member of self (e.g. an item in a Vec). The signature of the index method from the Index trait makes it impractical to implement it to have the behavior you described, as you'd have to store every value returned by index in self and ensure that the pointers remain valid until the MyStruct is dropped.
This use case does not match the intuition for Index. When I see myStruct[3], my intuition is that, just as for arrays, I'm getting a pointer to some already-initialized data. The interface for Index corroborates this intuition.
I can see two things that you might potentially be trying to achieve:
Getting nice indexing syntax for your datastructure.
In this case I would recommend against the premise of implementing Index and just provide a method that returns a f64 instead of an &f64 like so.
impl MyStruct {
pub fn index(&self, idx: usize) -> f64 {
self.start + (idx as f64) * self.step
}
}
You don't get the operators, which is good because somebody reading [] would be mislead into thinking they were getting a pointer. But you do get the functionality you want. Depending on your use cases you may want to rename this method.
Passing MyStruct to a parameter with Index bounds.
This is trickier with good reason. Index expects the data to be there before it asks for it. You can't generate and return it because index returns f64, and you can't generate it in the datastructure and return a pointer because it doesn't take a &mut self. You'd have to populate these values before the call to index. Some redesign would be in order, and the direction of that redesign would depend on the larger context of your problem.
I have a 2D vector that rejects indexing using i32 values, but works if I cast those values using as usize:
#[derive(Clone)]
struct Color;
struct Pixel {
color: Color,
}
fn shooting_star(p: &mut Vec<Vec<Pixel>>, x: i32, y: i32, w: i32, h: i32, c: Color) {
for i in x..=w {
for j in y..=h {
p[i][j].color = c.clone();
}
}
}
fn main() {}
When I compile, I get the error message
error[E0277]: the trait bound `i32: std::slice::SliceIndex<[std::vec::Vec<Pixel>]>` is not satisfied
--> src/main.rs:11:13
|
11 | p[i][j].color = c.clone();
| ^^^^ slice indices are of type `usize` or ranges of `usize`
|
= help: the trait `std::slice::SliceIndex<[std::vec::Vec<Pixel>]>` is not implemented for `i32`
= note: required because of the requirements on the impl of `std::ops::Index<i32>` for `std::vec::Vec<std::vec::Vec<Pixel>>`
If I change the code to have
p[i as usize][j as usize].color = c.clone();
Then everything works fine. However, this feels like it would be a really bizarre choice with no reason not to be handled by the Vec type.
In the documentation, there are plenty of examples like
assert_eq!(vec[0], 1);
By my understanding, if a plain number with no decimal is by default an i32, then there's no reason using an i32 to index shouldn't work.
Unlike Java, C# or even C++, numeric literals in Rust do not have a fixed type. The numeric type of a literal is usually inferred by the compiler, or explicitly stated using a suffix (0usize, 0.0f64, and so on). In that regard, the type of the 0 literal in assert_eq!(vec[0], 1); is inferred to be a usize, since Rust allows Vec indexing by numbers of type usize only.
As for the rationale behind using usize as the indexing type: a usize is equivalent to a word in the target architecture. Thus, a usize can refer to the index/address of all possible memory locations for the computer the program is running on. Thus, the maximum possible length of a vector is the maximum possible value that can be contained in a isize (isize::MAX == usize::MAX / 2). Using usize sizes and indices for a Vec prevents the creation and usage of a vector larger than the available memory itself.
Furthermore, the usage of an unsigned integer just large enough to refer all possible memory locations allows the removal of two dynamic checks, one, the supplied size/index is non-negative (if isize was used, this check would have to be implemented manually), and two, creating a vector or dereferencing a value of a vector will not cause the computer to run out of memory. However, the latter is guaranteed only when the type stored in the vector fits into one word.
The first conversion using 'as' compiles, but the second one using the 'From' trait does not:
fn main() {
let a: u64 = 5;
let b = a as usize;
let b = usize::from(a);
}
Using Rust 1.34.0, I get the following error:
error[E0277]: the trait bound `usize: std::convert::From<u64>` is not satisfied
--> src/main.rs:4:13
|
4 | let b = usize::from(a);
| ^^^^^^^^^^^ the trait `std::convert::From<u64>` is not implemented for `usize`
|
= help: the following implementations were found:
<usize as std::convert::From<bool>>
<usize as std::convert::From<std::num::NonZeroUsize>>
<usize as std::convert::From<u16>>
<usize as std::convert::From<u8>>
= note: required by `std::convert::From::from`
When I replace u64 with u8, there is no more error. From the error message, I understand that the From trait is implemented only for u8, but not for the other integer types.
If there is a good reason for that, then why shouldn't the conversion using 'as' should also fail to compile?
as casts are fundamentally different from From conversions. From conversions are "simple and safe" whereas as casts are purely "safe". When considering numeric types, From conversions exist only when the output is guaranteed to be the same, i.e. there is no loss of information (no truncation or flooring or loss of precision). as casts, however, do not have this limitation.
Quoting the docs,
The size of [usize] is "how many bytes it takes to reference any location in memory. For example, on a 32 bit target, this is 4 bytes and on a 64 bit target, this is 8 bytes."
Since the size depends on the target architecture and cannot be determined before compilation, there is no guarantee that a From conversion between a numeric type and usize is possible. An as cast, however, will always operate by the rules listed here.
For instance, on a 32-bit system, usize is equivalent to u32. Since a usize is smaller than a u64, there can be loss of information (truncation) when converting a u64 into a usize and hence a From conversion cannot exist. However, the size of a usize is always guaranteed to be 8 bits or greater and a u8 to usize From conversion will always exist.
As already mentioned, converting from a 64-bit value to a usize might cause truncation; you might lose data when a usize is 16 or 32 bits.
Fallable conversions are covered by the TryFrom trait, available in Rust 1.34:
use std::convert::TryFrom;
fn main() {
let a: u64 = 5;
let b = a as usize;
let b = usize::try_from(a);
}
See also:
How do I convert a usize to a u32 using TryFrom?
In this code:
fn unpack_u32(data: &[u8]) -> u32 {
assert_eq!(data.len(), 4);
let res = data[0] as u32 |
(data[1] as u32) << 8 |
(data[2] as u32) << 16 |
(data[3] as u32) << 24;
res
}
fn main() {
let v = vec![0_u8, 1_u8, 2_u8, 3_u8, 4_u8, 5_u8, 6_u8, 7_u8, 8_u8];
println!("res: {:X}", unpack_u32(&v[1..5]));
}
the function unpack_u32 accepts only slices of length 4. Is there any way to replace the runtime check assert_eq with a compile time check?
Yes, kind of. The first step is easy: change the argument type from &[u8] to [u8; 4]:
fn unpack_u32(data: [u8; 4]) -> u32 { ... }
But transforming a slice (like &v[1..5]) into an object of type [u8; 4] is hard. You can of course create such an array simply by specifying all elements, like so:
unpack_u32([v[1], v[2], v[3], v[4]]);
But this is rather ugly to type and doesn't scale well with array size. So the question is "How to get a slice as an array in Rust?". I used a slightly modified version of Matthieu M.'s answer to said question (playground):
fn unpack_u32(data: [u8; 4]) -> u32 {
// as before without assert
}
use std::convert::AsMut;
fn clone_into_array<A, T>(slice: &[T]) -> A
where A: Default + AsMut<[T]>,
T: Clone
{
assert_eq!(slice.len(), std::mem::size_of::<A>()/std::mem::size_of::<T>());
let mut a = Default::default();
<A as AsMut<[T]>>::as_mut(&mut a).clone_from_slice(slice);
a
}
fn main() {
let v = vec![0_u8, 1, 2, 3, 4, 5, 6, 7, 8];
println!("res: {:X}", unpack_u32(clone_into_array(&v[1..5])));
}
As you can see, there is still an assert and thus the possibility of runtime failure. The Rust compiler isn't able to know that v[1..5] is 4 elements long, because 1..5 is just syntactic sugar for Range which is just a type the compiler knows nothing special about.
I think the answer is no as it is; a slice doesn't have a size (or minimum size) as part of the type, so there's nothing for the compiler to check; and similarly a vector is dynamically sized so there's no way to check at compile time that you can take a slice of the right size.
The only way I can see for the information to be even in principle available at compile time is if the function is applied to a compile-time known array. I think you'd still need to implement a procedural macro to do the check (so nightly Rust only, and it's not easy to do).
If the problem is efficiency rather than compile-time checking, you may be able to adjust your code so that, for example, you do one check for n*4 elements being available before n calls to your function; you could use the unsafe get_unchecked to avoid later redundant bounds checks. Obviously you'd need to be careful to avoid mistakes in the implementation.
I had a similar problem, creating a fixed byte-array on stack corresponding to const length of other byte-array (which may change during development time)
A combination of compiler plugin and macro was the solution:
https://github.com/frehberg/rust-sizedbytes
This code:
use std::usize;
fn main() {
fn main() {
let a = usize::MAX;
println!("{:?}", a );
println!("{:?}", a as f64 );
}
returns
18446744073709551615
18446744073709552000
what is the best way to be aware of this loss of precision?
The conv crate was designed to handle this. The ValueFrom/ValueInto traits perform value-preserving conversions that will return an error if the input value cannot be exactly represented in the output type. For example:
/*!
Add this to your `Cargo.toml`:
```cargo
[dependencies]
conv = "0.3.2"
```
*/
extern crate conv;
use conv::prelude::*;
fn main() {
let a = std::u32::MAX;
println!("u32::MAX -> f32: {:?}", a.value_as::<f32>());
println!("u32::MAX -> f64: {:?}", a.value_as::<f64>());
}
Outputs:
u32::MAX -> f32: Err(PosOverflow(..))
u32::MAX -> f64: Ok(4294967295)
Alternately, you can replicate the checking conv does by hand: check that the input integer does not fall outside the range of integers the floating point type can exactly represent. Specifically:
f32: ±16,777,215
f64: ±9,007,199,254,740,991
Well, 64-bit floating point representation is accurate to 15–17 significant decimal digits, so you can estimate potential inaccuracy based on that. You could get more fancy to saying that such-and-such a float representation has a margin of error of x, so that that particular bit sequence represents values in the range self - x to self + x, but that’s so rarely a useful concept that you won’t be likely to find much code to assist you on it.