pyo3 icon indicating copy to clipboard operation
pyo3 copied to clipboard

Failure to extract borrowed class references in `experimental-async` function

Open davidhewitt opened this issue 1 month ago • 2 comments

The following code does not compile with the experimental-async feature enabled:

#[pyclass]
struct Value(i32);

#[pymethods]
impl Value {
    #[new]
    fn new(x: i32) -> Self {
        Self(x)
    }
}

#[pyfunction]
async fn add_two_values(obj: &Value, obj2: &Value) -> Value {
    Value(obj.0 + obj2.0)
}

The error is:

error[E0521]: borrowed data escapes outside of function
   --> tests/test_coroutine.rs:373:5
    |
373 |     #[pyfunction]
    |     ^^^^^^^^^^^^^
    |     |
    |     `py` is a reference that is only valid in the function body
    |     `py` escapes the function body here
    |     lifetime `'py` defined here
    |     argument requires that `'py` must outlive `'static`
    |
    = note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0716]: temporary value dropped while borrowed
   --> tests/test_coroutine.rs:373:5
    |
373 |     #[pyfunction]
    |     ^^^^^^^^^^^^-
    |     |           |
    |     |           temporary value is freed at the end of this statement
    |     creates a temporary value which is freed while still in use
    |     argument requires that borrow lasts for `'static`
    |
    = note: this error originates in the attribute macro `pyfunction` (in Nightly builds, run with -Z macro-backtrace for more info)

davidhewitt avatar Dec 09 '25 10:12 davidhewitt

I think it's because the extraction code is creating PyClassGuard containers with some lifetime which is not 'static. I am unsure if we can work around this, experimentation needed.

davidhewitt avatar Dec 09 '25 10:12 davidhewitt

I think it's potentially possible if the argument extraction happens inside the future rather than outside. The only downside I can see would be that all input arguments are currently in the extraction code as Borrowed<'py, 'py, PyAny> references would need to be converted to strong Py<PyAny> references inside the future's state; I think that's probably fine.

There's maybe an "optimization" where we could convert 'static data types eagerly and non-static types inside the future, but I think that might be confusing from control flow. Can defer that for the future.

davidhewitt avatar Dec 09 '25 11:12 davidhewitt