I came up with a function that is supposed to take any signed or unsigned integer and map it to a u8. Every negative value becomes 0 while every value above 255 should be 255. I know this is trivial, my goal is only to get an understanding on how generics work.
use std::convert::TryInto;
fn as_byte<T>(source: T) -> u8
where
T: TryInto<u8>,
T: PartialOrd<u8>,
{
if source < 0 {
return 0;
}
if source > 255 {
return 255;
}
source.try_into().unwrap_or(0)
}
I thought it would make sense to constrain my generic to whatever I actually want to do with source, so I declared that I want to be able to compare it to an u8 and be able to do a conversion to u8 (which should not fail if I catch a value out of range before).
If I call as_byte(1234i32), it will fail since there is no implementation of PartialOrd<u8> for i32. Even if there were an implementation, this will fail due to source being moved on the first comparison and then being used after move in the second check.
If I pass source by reference like as_byte(&1234i32) it gets even worse. Now the TryInto<u8> trait is not implemented either, since there is no From<&i32> for u8.
I understand why these issues are raised, but I can not figure out how to resolve them. I tried to change the constraints to where &'a T or changed the parameter to source: &T, or both. That left me with similar issues where traits for reference types were not implemented. This leaves me feeling like I didn't understand the concept of the borrowing/references correctly in the first place.
Where is the error in my thought process?
If
PartialOrd<u8> for i32existed, what should this method's declaration look like?Knowing that
PartialOrd<u8> for i32does not exist, how else should I constrain the type parameter? Why do I get the error "the trait `Foo` is not implemented for `&mut T`" even though T implements the trait? is related and somewhat answers my question - but I can (and probably should) not implement traits for primitive/std types, can I?