-
Notifications
You must be signed in to change notification settings - Fork 763
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
add pyclass hash
option
#4206
add pyclass hash
option
#4206
Conversation
981ec46
to
df991b6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks perfect to me! I love to see this, I've wanted this for a while and never got around to it 👍
Actually, one thought - should we make this only available with (If they really want a |
Interaction with frozen
is worth discussing now
Given that it can be implemented trivially if needed, I think that makes sense to take the safe route here 👍. On that note, should we also require |
Great question. So, quoting the Python docs:
I guess that wording is strong enough that we should indeed be encouraging users to define I'm happy to either block this PR on landing |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Feel free to decide what route to go down, also just one suggestion on wording...)
I think I might just implement the |
Co-authored-by: David Hewitt <mail@davidhewitt.dev>
Rebased. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks brilliant thank you! I have one small thought about a possible combined check for both eq
and frozen
at the same time, otherwise this looks ready to me 🎉
pyo3-macros-backend/src/pyclass.rs
Outdated
) -> Result<(Option<syn::ImplItemFn>, Option<MethodAndSlotDef>)> { | ||
if options.hash.is_some() { | ||
ensure_spanned!(options.frozen.is_some(), options.hash.span() => "The `hash` option requires the `frozen` option."); | ||
ensure_spanned!(options.eq.is_some(), options.hash.span() => "The `hash` option requires the `eq` option."); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For these error messages I think it's quite likely users might hit the frozen
error, fix that, and then hit the eq
error. If you agree, it might be nice if we also special-case if both are missing and emit an error message requesting both options so users can fix in a single compile.
Or we could keep it simple and just always check for both in one ensure_spanned
and just emit the combined message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, I added a variant to the ensure_spanned
macro that evaluates multiple branches and returns all errors.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Very elegant 💯
I don't get it, how accidental ??? If I mutate an object I expect the hash change. This seriously limit the use of this automatic derive. |
Mutating an object hash in Python is usually a mistake - e.g. because it breaks dictionaries. From the Python docs:
|
To add to that, in Rust you can't easily mutate an object while it's in a HashMap (only via interior mutability or unsafe) since it can control that you never get a &mut reference to it. Python can't offer such a guarantee, and therefore only lets you use immutable objects as keys or set items by default. Of course you can implement your own hash and wreak havoc. |
Yes of course like in Rust key inside a hashmap should not be modified in a way that change the hash but that a solf error from the user, it's NOT enforced at compile time even in Rust. Well I don't know exactly how python works, but this mean I can't use this derive if I have a rust type that CAN be mutable. My use case is pretty simple I want my Rust struct to be hashable, and I have serialization and deserialisation feature that somehow need to construct dummy value and then set state so they need to be mutable... I blame python pickle for asking user non sense. Anyway, the only requirement is the user should not mutate the key of a hashmap (like in rust) and that for me obvious, but require this at compile time is very annoying making this derive not very useful. |
If pickle is the reason why your class needs to be mutable, you should be able to use the |
it's impossible my object can't be construct without specific parameter that I don't have access anymore after and this doesn't change my main argument, any non trivial struct that could be mutable will not be able to use this derive. Please ignore my specific usecase and just accept this struct also have mutable feature that user will not use when the struct is in a dict. |
For more
dataclass
like usage and similar to #4202, this adds ahash
pyclass option to implement the__hash__
slot using the type'sHash
implementation.