Sometimes you may want to use an enum
rather than a generic type with a trait bound. For example:
enum Unsigned {
U16(u16),
U32(u32),
U64(u64),
}
struct Foo { x: Unsigned, ... };
One advantage of making a new type over implementing a new trait for existing types is that you can add foreign traits and inherent behavior to the new type. You can implement any traits you like for Unsigned
, like Add
, Mul
, etc. When Foo
contains an Unsigned
, implementing traits on Unsigned
doesn't affect the signature of Foo
like it would to add them as bounds on Foo
's parameter (e.g. Foo<T: Add<Output=Self> + PartialCmp + ...>
). On the other hand, you do still have to implement each trait.
Another thing to note: while you can generally always make a new type and implement a trait for it, an enum is "closed": you can't add new types to Unsigned
without touching the rest of its implementation, like you could if you used a trait. This may be a good thing or a bad thing depending on what your design calls for.
"Performance reasons" is a bit ambiguous, but if you're thinking of storing a lot of Unsigned
s that will all be the same internal type, and this:
struct Foo([Unsigned; 1_000_000]);
would waste a ton of space over storing a million u16
s, you can still make Foo
generic! Just implement From<u16>
, From<u32>
, and From<u64>
for Unsigned
and write this instead:
struct Foo<T: Into<Unsigned>>([T; 1_000_000]);
Now you only have one simple trait bound on T
, you're not wasting space for tags and padding, and functions that deal with T
can always convert it to Unsigned
to do calculations with. The cost of the conversion may even be optimized away entirely.
See Also