-
Notifications
You must be signed in to change notification settings - Fork 20
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce a Compare
trait in the cmp mod
#527
Comments
cc @Amanieu as a libs-team member and ever touch the |
Would it fit into existing collections like BTreeMap? |
@the8472 Using these I found a previous discussion in IRLO. It seems to be no blocker for adding a new generic parameter like the extension of allocator-api. But I'd break it down into a few steps:
This is, however, not a blocker for introducing the |
Implementing is not a blocker, checking whether the design is compatible would be imo, since it would be quite unfortunate to add a mechanism to the standard library which doesn't integrate with other parts of the library. |
@the8472 IIUC the integration is possible. You can check binary-heap-plus linked above as an example. And the allocator-api show a way for evolution the generic of existing collections. HashMap also has an |
finally a way to compare things in standard algorithms where you need extra state outside of the items you're comparing! |
I think also having a |
@programmerjake Yeah. Then if we follow the Hash/Hasher/BuildeHasher analogy, we may have:
I'd like to confirm that you're agree on the general direction, and ask if you have better names suggested. |
they have significant semantic differences, so I think we need both I don't think we need the |
If we add CustomEq we'd also need externalized hashing because a Map that uses custom key comparison can also need custom key hashing. |
Well. Then maybe for naming:
Yes. This is the trait bound I currently use. I saw your comment mention "with state" so wonder whether we need a builder. And
I think this is dedicated to |
looks good!
Imagine you have a |
since hashes don't easily combine and it's usually better to pass all input parts into a hashing algorithm and extract a hash at the end, maybe have an API like so: pub trait BikeshedHash<T: ?Sized> {
type Hasher<'a>: 'a
where
Self: 'a;
fn build_hasher(&self) -> Self::Hasher<'_>;
fn hash<'a>(&'a self, hasher: &mut Self::Hasher<'a>, value: &T);
fn finish<'a>(&'a self, hasher: Self::Hasher<'a>) -> u64;
} this allows recursive calls of struct StringPair(String, String);
struct CaseInsensitive(DefaultHasher);
impl BikeshedHash<StringPair> for CaseInsensitive {
type Hasher<'a> = RandomState;
fn build_hasher(&self) -> Self::Hasher<'_> {
self.0.build_hasher()
}
fn hash<'a>(&'a self, hasher: &mut Self::Hasher<'a>, value: &StringPair) {
self.hash(hasher, &value.0);
self.hash(hasher, &value.1);
}
fn finish<'a>(&'a self, hasher: Self::Hasher<'a>) -> u64 {
hasher.finish()
}
}
impl BikeshedHash<String> for CaseInsensitive {
type Hasher<'a> = RandomState;
fn build_hasher(&self) -> Self::Hasher<'_> {
self.0.build_hasher()
}
fn hash<'a>(&'a self, hasher: &mut Self::Hasher<'a>, value: &String) {
value.len().hash(hasher);
for b in value.bytes() {
let b = b.to_ascii_uppercase(); // I'm lazy, so skip unicode
b.hash(hasher);
}
}
fn finish<'a>(&'a self, hasher: Self::Hasher<'a>) -> u64 {
hasher.finish()
}
} |
An alternative approach would be a separate type along the lines of hashbrown's |
well, if only the completely external to the container approach were in std for all the other things that use comparison, e.g. |
I've updated the description "Solution sketch" for adding four traits (analogy to PartialEq/Eq/PartialOrd/Ord). For collections like
I wonder how we pass the customized Compare logic if we don't introduce a Compare trait. Or it's an implementation method with the Compare trait.
For user-defined type, we already require well-defined implementation. That said, if we wrap str in a new type and implement hash unaligned with equal, it would behave wrongly. |
For HashMap, I may need some time to work a clear solution. But it seems the internal HashMap(HashTable) can have a comparator as state to wrap the |
@Amanieu I looked into this solution today and noticed that we don't expose Anyway, having such a solution with the current status to HashSet/HashMap may unblock the concern "whether the design is compatible". |
Not at the moment: the recommendation is for people who need |
We discussed this during today's libs-API meeting. We're sympathetic to making custom comparisons more ergonomic, but to make a decision we need sketches of the potential broader API designs (including alternatives and how ergonomic they are) and how this would fit into collections and other APIs that use comparisons. A lot of the discussion in the meeting echoed comments from this thread. If we introduce a new trait in std it should also be usable by and with other std types and APIs. And a major potential user of that would be collections, sorting and iterators or collection contents.
Now writing this comment I notice we didn't actually spend much time on discussing your motivation. You say
Can you explain how just having the traits in core without further integration into the rest of the standard library would improve the problems you're facing? |
Proposal
Problem statement
We have
core::cmp::Reverse
to wrap a type for changing the ordering logic of a typeT
. However, under certain generic context, it would introduce unnecessary burden over the input typeT
whereT
can be qualified instead.Introducing a
Compare
trait can help in such a situation that qualified the typeT
as is, and accepting a new typeC: Compare
, acting as a comparator, to switch among different comparison functions.Motivating examples or use cases
We have functions like:
and use the
compare
traits:If using
core::cmp::Reverse
, it would require complex method forwarding to letReverse<T>
implement everythingT
does.Take a more complex case, we make a
PartialCompare
trait based on theCompare
traitand use it as:
You can imagine if we have to wrap all the types here with
Reverse
or similar approaches, it would be quite challenging to forward all the traits and make the type system happy.Solution sketch
Bringing
Compare
andPartialCompare
into the core lib would help in this case, where thecompare
crate is linked above, and thePartialCompare
implementation is inlined above.I'd first submit this issue to see if this is the way to go, further implement details can go as follow-ups.Add four traits:
Alternatives
Just use the
compare
crate or make a new crates.io crate.However, the
compare
crate isn't updates for more than nine years, and somehow the core lib hascmp
mod that already provides some functionality to support this use case. I wonder if it's suitable as a supplement to the core lib.Links and related work
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
Second, if there's a concrete solution:
The text was updated successfully, but these errors were encountered: