From a quick read what I gathered is there are essentially two ways you can accept a python dictionary:
&Bound<'py, PyDict>
where pyo3
automatically holds the GIL as long as your function runs.Py<PyDict>
, whenever you want to access it you have to get a hold of the GIL first with Python::with_gil
or similar.and two ways to work with it:
PyDictMethods
)PyDict
to a HashMap
(or other T: FromPyObject
)You can mix and match them as required, for example accepting a Bound
and working directly with the methods:
use pyo3::{prelude::*, types::PyDict};
#[pyfunction]
pub fn process_dict(map: &Bound<'_, PyDict>) -> PyResult<()> {
if let Some(value) = map.get_item("apple")? {
println!("Value for 'apple': {}", value);
} else {
println!("Key not found");
}
Ok(())
}
Which has the advantage of you not having to care about the GIL and also no overhead necessary to convert the Python dict to a Rust type. The disadvantages are that the GIL is held for the entire runtime of the function and you're limited to what the python interface has to offer.
Or accepting a GIL independent Py
and converting the value to a Rust type:
use std::collections::HashMap;
use pyo3::{prelude::*, types::PyDict};
#[pyfunction]
pub fn process_dict(map: Py<PyDict>) -> PyResult<()> {
Python::with_gil(|gil| {
let map: HashMap<String, i64> = map.extract(gil).unwrap();
if let Some(value) = map.get("apple") {
println!("Value for my 'apple': {}", value);
} else {
println!("Key not found");
}
});
Ok(())
}
Advantages include having precise control where the GIL is held and getting to work with Rust native types while the disadvantages are the added complexity of handling the GIL as well as an overhead incurred for converting the PyDict
to a HashMap
So to answer your questions directly:
How to solve this error? What is expected here and why?
Pass in a Python
object that prooves you have the GIL because it's needed to safely access the dictionary.
Do I have to use the
extract
method here, is there a simpler method?
No, not at all necessary, you can directly work with a &Bound<'_, PyDict>
and it's methods instead.
Is the
map.extract()
function expensive?
Somewhat, it has to copy and convert the python dictionary to a Rust type.