Skip to content

Conversation

@alvinisspicy
Copy link
Contributor

What does this PR do?

  • Adds #[cfg_attr(feature = "serde", serde(skip))] to the PhantomData marker fields in OpaqueColor, AlphaColor, and PremulColor.

Why is this needed?

  • Keeps the cs marker purely at the type level and out of the serialized representation.
  • Simplifies serialization formats so only the value components are required.
  • Avoids unnecessary serde bounds and makes it possible for downstream crates (like the toml crate) to serialize and deserialize these color types.

Behavioral changes:

  • No runtime behavior change as PhantomData is zero-sized and was not carrying data.
  • The serialized form for these types is unchanged in practice, except that any prior cs field (if present) is now ignored.

Copy link
Member

@DJMcNab DJMcNab left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The relevant impl on the serde side is:
https://docs.rs/serde/1.0.228/src/serde/core/ser/impls.rs.html#117-128

That is, the serialised form of the cs field will never actually provide any "type safety", so this change is very low risk.
There are reasonable arguments for wanting to express the colour space in the serialised form. That adds extra type safety, but could easily cause confusion (e.g. if someone tries to change the color space and instead gets a crash). But the UX for this serialisation won't be great; it will just be opaque numbers (pun not intended). That isn't an issue in this PR, but if we were to make breaking changes here, we should probably batch them with this change.

These impls were added in #61; @waywardmonkeys can you speak to whether the "type safety" was ever reasoned about here?

This PR looks good, other than the one changelog nit. Once that's done, I'll be happy to merge this. Thanks!

CHANGELOG.md Outdated

### Changed

* Skip serializing/deserializing `PhantomData` marker fields of `OpaqueColor`, `AlphaColor`, and `PremulColor` when the `serde` feature is enabled.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs the PR and author links (don't forget to also add these to the link reference definitions at the bottom)

@waywardmonkeys
Copy link
Collaborator

serde was only added because it was needed by peniko ... I think I wish that we hadn't added it and just bit the bullet on removing it from Peniko.

I don't know if I feel comfortable removing this ... without it, you can't know what was serialized. But then, you don't really know anyway from it, I guess.

Thoughts, @tomcur ?

@alvinisspicy What're you using serde + color for?

@alvinisspicy
Copy link
Contributor Author

Thanks for the feedback and context!

@waywardmonkeys, I am not sure if you were referring specifically to adding serde(skip) on the PhantomData marker fields when you wrote:

... I don't know if I feel comfortable removing this ... without it, you can't know what was serialized. But then, you don't really know anyway from it, I guess. ...

On the question of “type safety” from the cs field:

  • Because cs is a PhantomData compile-time marker, serde does not encode any information about the generic CS into the serialized data.
  • In practice, this means the cs field is always encoded as a zero-sized / unit value (for example, null in JSON), with no runtime distinction between different color spaces.
  • Changing the CS generic on the Rust side without changing the serialized data already compiles and runs today, which shows that cs does not provide additional type safety in the serialized representation.

This PR does not cause any breaking changes. By default, serde will skip any additional unknown fields during deserialization (https://serde.rs/container-attrs.html#deny_unknown_fields). If, in the future, there is interest in incorporating the color space into the serialized format in a meaningful way (e.g., a tagged enum or string name), that should be introduced in a separate, explicitly breaking change with a clearer UX around how color spaces are encoded.

For additional context, I encountered this issue while trying to deserialize color::AlphaColor using the toml crate. The toml crate does not have a useful representation for PhantomData and attempting to deserialize it leads to runtime errors. This can be worked around with a newtype that omits cs and reconstructs the marker in code; however, adding serde(skip) to the PhantomData fields seems like a cleaner and more idiomatic solution, and matches common practice for marker-only fields.

@alvinisspicy alvinisspicy force-pushed the serde_skip_PhantomData branch from 68221c3 to 5e51403 Compare December 15, 2025 18:54
Copy link
Member

@tomcur tomcur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to skip these fields. This will be a breaking change (e.g. deserializing using the old version will expect the cs field to be present, throwing an error otherwise).

@tomcur tomcur added this pull request to the merge queue Dec 17, 2025
@tomcur
Copy link
Member

tomcur commented Dec 17, 2025

Thanks!

Merged via the queue into linebender:main with commit 1d961a3 Dec 17, 2025
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants