Skip to content

Conversation

@Arnavion
Copy link

@Arnavion Arnavion commented Nov 17, 2025

Since the map should never store elements with a count of 0, it is better to store the count as NonZero<usize> instead of usize. This also allows the Iter impl to become smaller and simpler.

More importantly, before this change, it was possible to insert an element into the set with a count of zero using insert_times(val, 0), which would've produced misbehavior such as contains(&val) returning true. The use of NonZero makes this bug pop out and less likely to be reintroduced in the future. Now insert_times(val, 0) is correctly treated as a no-op.

Another source of an element with a count of zero was doing insert_times(val, usize::MAX); insert_times(val, 1); with overflow checks disabled, as they are by default in release mode. Now insert_times() checks for overflow of the inserted element count as well as the size of the whole set.

There is a user-visible change in the signature of distinct_elements in that it now surfaces NonZero<usize> instead of usize. Other API are unchanged.

This change also fixes a few clippy lints added over the years, about elided '_ lifetimes, the bounds of contains's Q parameter being specified twice, and missing edition key in the crate manifest.


I know maintainer has not been present since 2020 and this is unlikely to be merged. I'm making this PR just so that anyone who stumbles upon this repository and wants to use it anyway is at least aware that this bug exists.

Also sent to modern-multiset

Since the map should never store elements with a count of 0, it is better to
store the count as `NonZero<usize>` instead of `usize`. This also allows
the `Iter` impl to become smaller and simpler.

More importantly, before this change, it was possible to insert an element
into the set with a count of zero using `insert_times(val, 0)`,
which would've produced misbehavior such as `contains(&val)` returning `true`.
The use of `NonZero` makes this bug pop out and less likely to be reintroduced
in the future. Now `insert_times(val, 0)` is correctly treated as a no-op.

Another source of an element with a count of zero was doing
`insert_times(val, usize::MAX); insert_times(val, 1);` with overflow checks
disabled, as they are by default in release mode. Now `insert_times()` checks
for overflow of the inserted element count as well as the size of
the whole set.

There is a user-visible change in the signature of `distinct_elements`
in that it now surfaces `NonZero<usize>` instead of `usize`. Other API are
unchanged.

This change also fixes a few clippy lints added over the years,
about elided `'_` lifetimes, the bounds of `contains`'s `Q` parameter being
specified twice, and missing `edition` key in the crate manifest.
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