What is the idiomatic way to deal with mixed integer types?
TL;DR: Explicit casts can feel annoying and verbose in some situations. Is there a better way to deal with mixed integer types?
Unexpected integer promotion is a common source of error in C and C++, thus Rust (along with other languages) made the decision to not have any sort of implicit integer casts.
That being said, I feel like this leads to some annoyance in some situations.
Example 1: Dithering
Here, I want to compute the difference between two u8 pixel channels. Since it can range -255..=255 I need to store it in a i16.
Then I want to multiply it by a float, so a cast to f32. Finally, I want to add it to the neighbor pixel and saturate the result, thus more casts and a clamp.
struct Pixel {
r: u8,
g: u8,
b: u8,
}
// ...
let err_r = (choosen_pixel.r as i16) - (wanted_pixel.r as i16);
// ...in another function
let err_r_to_apply = ((err_r as f32) * 7.0 / 16.0) as i16;
neighbor_pixel.r = ((neighbor_pixel.r as i16) + err_r_to_apply).clamp(u8::MIN as i16, u8::MAX as i16) as u8
Now, some of these casts can be considered unnecessary.
I could compute err_r as a f32 directly and skip the first i16 step entirely. And use integer literals in clamp instead of u8 constants.
Still, 5 casts would remain.
Example 2: Win32
I want to list all the formats that are currently in the clipboard using the windows crate.CountClipboardFormats returns the number of formats as an i32. Rust wants every size to be usize. GetUpdatedClipboardFormats takes an out pointer to a u32.
let count = CountClipboardFormats() as u32;
let mut out_count = 0u32;
let mut formats = vec![0; count as usize];
GetUpdatedClipboardFormats(&mut formats, &mut out_count).unwrap();
if out_count > count {
formats.truncate(out_count as usize);
}
This can be chalked up to the Win32 having a bad interface. According to microsoft's documentation, the function was introduced in ~2000. Thus, 32 bit CPUs were the norm and u32 mapped to usize at the time this API was written.
In any case, it seems to be limited to ~16000 registered clipboard formats.
But the issues don't end there. GetUpdatedClipboardFormats returns formats as [u32], thus a format is a u32.
But the standard format constants are defined as newtype : pub struct CLIPBOARD_FORMAT(pub u16);
This effectively mean I can't use the constants in pattern matching easily with the values I have!
I feel like using Win32 is unergonomic with Rust. The windows crate tries to make some APIs more "Rusty" but it ends up causing more pain points somewhere else, such as creating incompatible types where there was none previously.
Conclusion
Integer widening has been discussed multiple times before. Especially when it comes to indexing with anything other than usize. But it seems the conclusion is that it would cause some bigger issues: breaking interference, requiring special case behavior, introducing potential logic bugs...
I feel like .into doesn't really make the code less noisy or more readable, and could potentially lead to some confusing inference.
Am I missing something, either in the language or in the standard library?
Is there a more idiomatic way to deal with mixed integer types?