Why the compilier fails to detect niche here?

I was trying to imagine a way to introduce niche optimization in a sRGBA color type.
The idea is that when the alpha channel is zero, then the RGB values don't matter and can be zero.

The Repr enum represents internally the color value. The Transparent variant represents the color (0, 0, 0, 0).

#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Color(Repr);
  1. First attempt, Transparent is a unit variant.
#[derive(Clone, Copy)]
enum Repr {
    Transparent,
    Color([u8; 3], NonZero<u8>),
}
  1. Explicitly set the Transparent's fields to match the Color variant's fields but with unit type.
#[derive(Clone, Copy)]
enum Repr {
    Transparent([(); 3], ()),
    Color([u8; 3], NonZero<u8>),
}
  1. Explicitly set the Transparent's fields to match the Color variant's fields but with a type that can only be 0.
#[derive(Clone, Copy)]
enum Repr {
    Transparent([Zero; 3], Zero),
    Color([u8; 3], NonZero<u8>),
}

#[derive(Clone, Copy)]
#[repr(u8)]
enum Zero {
    Zero = 0,
}

It turns out that in the cases 1. and 2., there is no niche optimization, but in the last case, there is.

In case 1., since there is no field in Repr::Transparent, I was expecting the compiler to introduce niche optimization. So why isn't it the case?

It's because all variants must have the same niche, but the Transparent variant has no niche. When using

#[derive(Clone, Copy)]
enum Repr {
    Transparent,
    Color([u8; 3], NonZero<u8>),
}

and the variant is Transparent, then the rest of the enum is just padding. Padding can take any value, which is the opposite of what a niche is.

1 Like

As far as I can tell, it does— The size of Repr is 4, which means that the compiler is successfully using the niche in NonZero to store the discriminant for the Repr enum.

4 Likes

When I tried to replicate the OP on the playground only the third example is 5 bytes, the other two are 4 bytes big

1 Like

Oh! I forgot about the Repr discriminant, so yes you're right.
Actually I was thinking about niche for Option.

assert_eq!(size_of::<Color>(), size_of<Option<Color>>);

You should be able to make this work with

#[derive(Clone, Copy)]
#[repr(transparent)]
pub struct Color(NonZero<u32>);

and picking something other than pure black for the transparent “color”.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.