Conversation
981ec46 to
df991b6
Compare
davidhewitt
left a comment
There was a problem hiding this comment.
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 |
davidhewitt
left a comment
There was a problem hiding this comment.
(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. |
davidhewitt
left a comment
There was a problem hiding this comment.
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.
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.
I agree, I added a variant to the ensure_spanned macro that evaluates multiple branches and returns all errors.
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
dataclasslike usage and similar to #4202, this adds ahashpyclass option to implement the__hash__slot using the type'sHashimplementation.