Integer type conversions seem to come up all the time.
Maybe it is unavoidable - but but wish there was less 'friction'.
I think the problem is that array indexes have to be usize - len() also returns usize - which may not be the best choice for your own types (usually is not) - or types in the libraries you call.
I feel I have too many try_into().unwrap() and - when I'm feeling careless - "as u16" in my code.
E.g. the the ncurses crate expects screen coordinates (row & column) to be i32. Crossterm expects u16 for same.
crossterm::queue!(
stdout(),
cursor::MoveTo(
(i % WIDTH).try_into().unwrap(),
(i / WIDTH).try_into().unwrap()
),
style::PrintStyledContent(c),
)
.ok();
(i % WIDTH, i / WIDTH) is already a substantial concept in itself. You might as well finish the job and wrap it up in a nice package that is thoughtful about numeric ranges.
fn index_to_xy(index: usize) -> (u16, u16) {
assert!(index < WIDTH * HEIGHT);
// Cannot overflow after the assertion,
// because WIDTH and HEIGHT are each less than u16::MAX
(
(i % WIDTH) as u16,
(i / WIDTH) as u16
)
}
Use whichever of as or .try_into().unwrap() you like in this function — in either case it is certain that the conversion cannot overflow/wrap as long as WIDTH and HEIGHT are appropriately set. Now callers never have to worry about anything but “the index is in range”.
And this is where you have a problem. As someone who spent may, many days debugging problems created by C and C++ “frictionless” conversions I welcome that change.
Yes, there ways to reduce the boilerplate, but you want there to be friction. Even in something as mundane as terminal coordinates.
E.g. Midnight commander couldn't tolerate terminals wider than 256 characters wide — precisely because there are no friction and thus it's hard to see where exactly 320 is turning into 64.
What you want is to change either ncurses or crossterm to fix the issue, not change the language to hide it. Or maybe add some wrappers if both are unchangeable.