I have a variable
x defined as
u32. However, it has to be casted to
u64 in its usages. My question is that should I really define
u32 and do type casting when
x is used or should I just define
u64 in the beginning? Is there any (even tiny) benefit of defining
u32 in my use case?
I have a variable
You normally choose your integer type depending on the expected size range and any requirements from functions you will be calling, so I would answer your question with another question. What made you choose to define
x as a
u32 in the first place?
If you know you'll only ever be using numbers up to 4 billion and most of the times
x is used it'll be a
u32 then I'd just do the casts for the few times it's needed as a
u64. Alternatively, if you need it as a
u64 all the time then maybe it should have been a
u64 to begin with.
It depends on your use case. If you're storing it in a struct and space it tight, that might be beneficial. Sometimes 32bit math is faster than 64bit. Etc. If in doubt, measure and see.
Side note: To go from
u64, you can use
Into instead of casting:
let a = 2u32; let b: u64 = a.into();
x's values are within the range of
u32. However, I need to calculate
id % x and
id is of
u64 so I need to cast
u64 vs defining
u64 in the beginning, will there be extra costs?
Does casting smaller integer type to larger integer type has extra costs compared to defining a larger integer type in the beginning?
I'd expect yes but extremely minor: a stack / register allocation large enough for a u64, and then a bitwise copy to that stack space / register.
Should take a couple of ns each time.
It's highly doubtful you'll notice any difference in any real world scenario because a cast from 32 to 64-bit integers is a single highly optimised instruction. It will almost certainly blend into the noise for a real app and you are more likely to get performance gains by reducing unnecessary function calls/copies or just using a better algorithm.
On a 64-bit system the compiler may even be using 64-bit registers already and just ignoring the top bits.
You may find some differences when working in extreme situations. For example, if you've got gigabytes of these
u32's it'll take twice as long to just move them from RAM to the processor. You may also notice you can't process as many integers in bulk because SIMD can process more 32-bit integers in a single instruction than 64-bit integers.
BTW, what are the differences among different type casting methods:
Into are reflexive, and implementing one implies the other. Also, as I just learned, you can't implement a foreign trait on a foreign type. So generally you'll implement
From<Foreign> for Native. But
as is reserved for primitive types which can be cast as each other.
Into are generally intended to be lossless, infalliable conversions.
as on the other hand can discard data and lead to bugs in some situations, for example casting a
u64 to a
usize may truncate the value on a 32-bit host. For integer casts in specific, using
into() signals that there's no possible loss of data (
From<u64> is not implemented for
u32 for example).
I notice that on a 64-bit machine, the following doesn't work. I though
usize is essentially
u32 on 32-bit machines and
u64 on 64-bit machines. So does it mean that even
usize is storage-equivalent to
u64 on a 64-bit machine, it is still treated as a different type. In other words,
usize is NOT a type alias.
let x: usize = 1; u64::from(x)
That is correct. If usize we're a type alias then there could be code that would compile on 32 bit architectures but not on 63 bit architectures, which would be painful.
This could never work because if there ever is a 128-bit arch, the
u64::from(x) would no longer fit. So in order to preserve future compatibility, this cannot be implemented in general.
For a local variable, just use whatever width makes the code around it the most natural. If that's
u64 in your case because that's how it's used, just do that.
Optimizing variable sizes is really only worth bothering for things of which you're storing many instances.
Note that the "many" here may differ from your intuition. Thausands is usually not considered many. Millions is considered many depending on the context, but not always. It's rare to not consider billions as many.