-
Notifications
You must be signed in to change notification settings - Fork 902
Description
Python supports unpacking all objects which support the Sequence protocol. I believe it does so by simply calling __getitem__ for consecutive integer indices.
Currently in PyO3 only tuple objects can be extracted as Rust tuples. I think it'd be nice to have an unpack method which would emulate Python's unpacking behavior and support all objects which implement the Sequence interface.
I'd implement it as a utility trait:
trait Unpackable<'py>: Sized {
fn unpack(obj: Borrowed<'_, 'py, PyAny>) -> PyResult<Self>;
}Which can be implemented for tuples and arrays up to a certain size.
impl<'py, T0, T1, T2> Unpackable<'py> for (T0, T1, T2)
where
T0: for<'a> FromPyObject<'a, 'py>,
T1: for<'a> FromPyObject<'a, 'py>,
T2: for<'a> FromPyObject<'a, 'py>,
{
fn unpack(obj: Borrowed<'_, 'py, PyAny>) -> PyResult<Self> {
let t0 = match obj.get_item(0)?.extract::<T0>() {
Ok(v) => v,
Err(e) => return Err(e.into()),
};
let t1 = match obj.get_item(1)?.extract::<T1>() {
Ok(v) => v,
Err(e) => return Err(e.into()),
};
let t2 = match obj.get_item(2)?.extract::<T2>() {
Ok(v) => v,
Err(e) => return Err(e.into()),
};
Ok((t0, t1, t2))
}
}I used match here because I couldn't get the Into<PyErr> try operator inference to work on extract in this example.
The trait does not support depending on the 'a lifetime of the object because get_item creates new objects which are tied to the scope of the unpack function.
Additionally, this function can use __len__ for better error messages. But that would technically deviate from the official implementation: Python allows returning nonsensical lengths in __len__, unpacking will work correctly regardless