Skip to content

Derive Debug, Clone, Copy, PartialEq, Eq for the column descriptors - #32

Merged
emilk merged 2 commits into
release-0.6from
emilk/column-desc-derives
Aug 27, 2026
Merged

Derive Debug, Clone, Copy, PartialEq, Eq for the column descriptors#32
emilk merged 2 commits into
release-0.6from
emilk/column-desc-derives

Conversation

@emilk

@emilk emilk commented Aug 27, 2026

Copy link
Copy Markdown
Member

Related

What

DynColumnDesc derives Clone, Copy, Debug, Eq, PartialEq.

ColumnDesc gets the same set hand-written, because #[derive] would put each trait's bound on L. A descriptor holds no L — only a PhantomData<fn() -> L>, which is Copy, Eq, and Debug whatever L is. Debug skips the PhantomData and prints just the three real fields.

to_dyn now takes self by value, since Self: Copy (clippy's wrong_self_convention asks for it). desc.to_dyn() is unchanged at the call site.

One thing worth checking against your use case: the issue's motivating example compares descriptors from two different structs, so record_type differs and full-struct equality would be false where the current .name == .name is true. Comparing whole descriptors is the stricter check, not a drop-in for that assertion. Say the word if you want a name-and-type-only comparison instead of (or alongside) PartialEq.

Testing

New test covers Copy, both equality directions including metadata taking part, and that Debug names the fields without leaking PhantomData. It uses a ColumnDesc<Utf8>, and Utf8 derives nothing, so those lines only compile if the impls carry no bound on L.

Compatibility

Additive, except to_dyn taking self, which only affects explicit ColumnDesc::to_dyn(&x) calls. to_dyn is unreleased.

🤖 Generated with Claude Code

emilk and others added 2 commits August 27, 2026 17:37
`DynColumnDesc` derives `Clone, Copy, Debug, Eq, PartialEq`.
`ColumnDesc` gets them hand-written: `#[derive]` would put each trait's
bound on `L`, but a descriptor holds no `L` — only a
`PhantomData<fn() -> L>`, which is `Copy`, `Eq`, and `Debug` whatever
`L` is. `Debug` skips the `PhantomData`.

`to_dyn` now takes `self` by value, since `Self: Copy`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Uses a `ColumnDesc<Utf8>` for the `Debug` and `Eq` checks: `Utf8` itself
derives nothing, so those lines only compile because the impls put no
bound on `L`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@emilk emilk mentioned this pull request Aug 27, 2026
@emilk
emilk marked this pull request as ready for review August 27, 2026 15:39
@emilk
emilk merged commit 2f85eb2 into release-0.6 Aug 27, 2026
6 checks passed

@emilk emilk left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Post-merge review, as part of reviewing the 0.6 stack for #30. Clippy and tests are clean at the tip of release-0.6. Hand-writing the four impls is the right call, and using ColumnDesc<Utf8> in the test — where Utf8 derives nothing — is a compile-time proof that the impls carry no bound on L, which no runtime assertion could give you.

PartialEq on the metadata disagrees with arrow_field() equality. metadata is semantically a map but is compared as an ordered slice. Verified at the tip of release-0.6:

const A: ColumnDesc<Utf8> = ColumnDesc::new_with_metadata("R", "c", &[("a", "1"), ("b", "2")]);
const B: ColumnDesc<Utf8> = ColumnDesc::new_with_metadata("R", "c", &[("b", "2"), ("a", "1")]);

A == B                              // false
A.arrow_field() == B.arrow_field()  // true

Duplicate keys go the other way: &[("a", "1"), ("a", "2")] yields arrow_metadata() == {"a": "2"} while PartialEq sees two entries. So descriptors producing byte-identical arrow fields can compare unequal, and vice versa. The derive emits attribute order so only hand-written descriptors bite — which is the audience #23/#24 opened the door for. Either compare as maps (sorted pairs, no allocation, they are &'static) or document that the metadata is compared as written.

Eq without Hash. The obvious use for Eq on a descriptor is keying a map ("which columns have I handled"), and that needs Hash. Three lines, no bound on L. It has to match whatever the metadata comparison above settles on, which is the usual reason to sort that question first.

Minor: Copy makes DynColumnDesc::from(desc) (no &) the natural spelling, and it still doesn't compile — only From<&ColumnDesc<L>> exists.

— Claude

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.

1 participant