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 x
as u32
and do type casting when x
is used or should I just define x
as u64
in the beginning? Is there any (even tiny) benefit of defining x
as u32
in my use case?
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 u32
to u64
, you can use Into
instead of casting:
let a = 2u32;
let b: u64 = a.into();
I define x
as u32
because 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 x
to u64
. Casting x
to u64
vs defining x
as 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: T::from
, as
and into
?
From
and 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.
Implementing From
will result in the Into
implementation but not vice-versa. See also this documentation.
From
and 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.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.