unexpected behavior implementing __hash__ and __eq__ for collection inclusion between set and list #4681
-
Given the following Python (3.10) implementation: class PySetString:
def __init__(self, string):
self.string = string
def __eq__(self, o):
if isinstance(o, PySetString):
return self.string == o.string
elif isinstance(o, str):
return self.string == o
return False
def __hash__(self):
return hash(self.string) The following behavior is observed: >>> "a" in [PySetString("a")]
True
>>> "a" in {PySetString("a")}
True That is, implementing Given this Rust implementation: #[pyclass]
#[derive(Clone)]
struct RustSetString(String);
#[pymethods]
impl RustSetString {
#[new]
fn new(s:String) -> PyResult<Self> {
Ok(RustSetString(s))
}
fn __eq__(&self, other: Bound<PyAny>) -> PyResult<bool> {
if let Ok(rhs) = other.extract::<RustSetString>() {
return Ok(self.0 == rhs.0);
} else if let Ok(rhs) = other.downcast::<PyString>() {
return Ok(self.0 == rhs.to_str().unwrap());
}
Ok(false)
}
fn __hash__(&self) -> u64 {
let mut s = DefaultHasher::new();
self.0.hash(&mut s);
s.finish()
}
} The following behavior is observed: >>> "a" in [RustSetString("a")]
True
>>> "a" in {RustSetString("a")}
False I would think it's something dumb I'm doing if both comparisons failed, but only the set comparison does. It's also possible I don't understand Python comparison operators but the Python implementation seems to work well enough. Is this expected behavior? If so, is there a way to ensure both comparisons work in the same way as the Python implementation? EDIT: This is on pyo3 v0.22.2. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
In your Rust hasher you use a different hasher than the one Python uses, so it makes sense that the hash values aren't the same. You'd have to forward to Python's hasher if you want this to work. |
Beta Was this translation helpful? Give feedback.
In your Rust hasher you use a different hasher than the one Python uses, so it makes sense that the hash values aren't the same. You'd have to forward to Python's hasher if you want this to work.