-
Notifications
You must be signed in to change notification settings - Fork 275
Improve generic usage of Pixel trait #996
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
|
Ah neat, when I originally wrote this I wasn't aware of CastFromPrimitive. This makes more sense. |
I just defined it in this PR. (it could be better to implement it upstream, in the num crate). |
|
Aha, didn't finish scrolling :) |
|
Looks like the changes need to be made to some of the tests as well. |
lu-zero
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be noted that it is similar to the yet-to-stabilize try_from()
Here, in non-generic code, we just use |
In order to use either u8 or u16 for plane components, a Pixel trait
regroups several "capabilities" that a generic pixel type must support,
through trait inheritance.
That way, a generic function can just use the Pixel type, without
listing them all:
fn f<T: Pixel>(...) { ... }
However, trait inheritance cannot express constraints about T for other
types (like "i32: AsPrimitive<T>"). As a consequence, callers had to
specify these additional constraints manually:
where
T: Pixel,
i32: AsPrimitive<T>,
u32: AsPrimitive<T>,
This is redundant, because if T is a Pixel, then i32 and u32 necessarily
implement AsPrimitive<T> (since Pixel is only implemented for u8 and
u16).
To express these relationships, create a new trait,
CastFromPrimitive<T>, representing the inverse of AsPrimitive<T>.
This paves the way to make Plane (and many other structures) generic
over the pixel type conveniently.
Since Pixel is only implemented for u8 and u16 types, it is always possible to cast a Pixel to an integral primitive type. However, the compiler could not know this, because in theory any other type could also implement the Pixel trait. To enforce this restriction, make the (main) integral primitive types implement CastForPrimitive<T> for any T which implements Pixel. That way, the compiler knows that a Pixel (whatever its concrete type) can always be cast to an integer. Then, rewrite the constraints on convert_slice_2d() to use CastFromPrimitive, so that callers need not to add redundant constraints if T implements Pixel.
|
Thanks, quickly merging before we break it again :) |
In order to use either
u8oru16for plane components, aPixeltrait regroups several "capabilities" that a generic pixel type must support, through trait inheritance.That way, a generic function can just use the Pixel type, without listing them all:
However, trait inheritance cannot express constraints about
Tfor other types (likei32: AsPrimitive<T>). As a consequence, callers had to specify these additional constraints manually:where T: Pixel, i32: AsPrimitive<T>, u32: AsPrimitive<T>,This is redundant, because if
Tis aPixel, theni32andu32necessarily implementAsPrimitive<T>(since Pixel is only implemented foru8andu16).To express these relationships, create a new trait,
CastFromPrimitive<T>, representing the inverse ofAsPrimitive<T>.This paves the way to make
Plane(and many other structures) generic over the pixel type conveniently.Since Pixel is only implemented for
u8andu16types, it is always possible to cast aPixelto an integral primitive type. However, the compiler could not know this, because in theory any other type could also implement thePixeltrait.To enforce this restriction, make the (main) integral primitive types implement
CastForPrimitive<T>for anyTwhich implementsPixel. That way, the compiler knows that aPixel(whatever its concrete type) can always be cast to an integer.Then, rewrite the constraints on
convert_slice_2d()to useCastFromPrimitive, so that callers need not to add redundant constraints ifTimplementsPixel.