I have the following example:
use pyo3::prelude::*;
use std::collections::{HashMap, HashSet};
use std::sync::RwLock;
#[pyclass]
struct Rustex {
map: RwLock<HashMap<String, String>>,
contexts: RwLock<HashSet<String>>,
}
#[pymethods]
impl Rustex {
#[new]
fn new() -> Self {
Rustex {
map: RwLock::new(HashMap::new()),
contexts: RwLock::new(HashSet::new()),
}
}
fn acquire_mutex<'a>(
&mut self,
py: Python<'a>,
mutex_name: String,
context: String,
) -> PyResult<&'a PyAny> {
pyo3_asyncio::async_std::future_into_py(py, async move {
let mut map = self.map.write().unwrap();
Ok(Python::with_gil(|py| py.None()))
})
}
}
#[pymodule]
fn rustex(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<Rustex>()?;
Ok(())
}
When compiled, the error says:
error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
--> src/main.rs:27:64
|
22 | &mut self,
| --------- this data with an anonymous lifetime `'_`...
...
27 | pyo3_asyncio::async_std::future_into_py(py, async move {
| ________________________________________________________________^
28 | | let mut map = self.map.write().unwrap();
29 | | Ok(Python::with_gil(|py| py.None()))
30 | | })
| |_________^ ...is used here...
|
note: ...and is required to live as long as `'static` here
--> src/main.rs:27:9
|
27 | pyo3_asyncio::async_std::future_into_py(py, async move {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: `'static` lifetime requirement introduced by this bound
--> /Users/shep/.cargo/registry/src/github.com-1ecc6299db9ec823/pyo3-asyncio-0.16.0/src/async_std.rs:318:46
|
318 | F: Future<Output = PyResult<T>> + Send + 'static,
| ^^^^^^^
If I make self
'static
then gil
isn't happy:
error[E0597]: `_ref` does not live long enough
--> src/main.rs:11:1
|
11 | #[pymethods]
| ^^^^^^^^^^^-
| | |
| | `_ref` dropped here while still borrowed
| borrowed value does not live long enough
| argument requires that `_ref` is borrowed for `'static`
|
= note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0597]: `gil` does not live long enough
--> src/main.rs:11:1
|
11 | #[pymethods]
| ^^^^^^^^^^^-
| | |
| | `gil` dropped here while still borrowed
| borrowed value does not live long enough
| argument requires that `gil` is borrowed for `'static`
|
= note: this error originates in the attribute macro `pymethods` (in Nightly builds, run with -Z macro-backtrace for more info)
Is there a way to either
self
reference not 'static
but still available in the asyncgil
live long enoughI am using the following crates:
pyo3 = { version = "0.16.5", features = ["extension-module"] }
pyo3-asyncio = { version = "0.16", features = ["attributes", "async-std-runtime"] }
async-std = "1.9"
As correctly pointed out by @ShepMaster, the solution here is to Arc the self-reference in order to allow for multiple ownership. Therefore Rustex is now treated as a 'core'-object and any references go via the Arc. Therefore the structure becomes:
#[derive(Default)]
struct RustexCore {
map: RwLock<HashMap<String, String>>,
contexts: RwLock<HashSet<String>>,
}
#[pyclass]
#[derive(Default)]
struct Rustex(Arc<RustexCore>);
Now any function will access one of the references and get access to the object by cloning the reference:
#[pymethods]
impl Rustex {
fn acquire_mutex<'a>(
&self,
py: Python<'a>,
mutex_name: String,
context: String
) -> PyResult<&'a PyAny> {
let core = Arc::clone(&self.0);
From there on this core
can be used also in the async
function.
The full code is:
use pyo3::prelude::*;
use std::{
collections::{HashMap, HashSet},
sync::{Arc, RwLock},
};
#[derive(Default)]
struct RustexCore {
map: RwLock<HashMap<String, String>>,
contexts: RwLock<HashSet<String>>,
}
#[pyclass]
#[derive(Default)]
struct Rustex(Arc<RustexCore>);
#[pymethods]
impl Rustex {
#[new]
fn new() -> Self {
Self::default()
}
fn acquire_mutex<'a>(
&self,
py: Python<'a>,
mutex_name: String,
context: String,
) -> PyResult<&'a PyAny> {
let core = Arc::clone(&self.0);
pyo3_asyncio::async_std::future_into_py(py, async move {
let mut map = core.map.write().unwrap();
Ok(Python::with_gil(|py| py.None()))
})
}
}
#[pymodule]
fn rustex(_py: Python, m: &PyModule) -> PyResult<()> {
m.add_class::<Rustex>()?;
Ok(())
}