如何更改一个值,使其在 Rust 中没有静态生命周期

How to change a value so that it doesn't have static lifetime in Rust

我有以下使用 PyO3 调用 python 函数并获得结果的函数(在本例中,int 被分配给 i32):

fn run_python<'a, T: FromPyObject<'a> + Clone>(func_name: &str) -> Result<T, ()> {
    Python::with_gil(|py| {
        let pyapi = match py.import("pyapi") {
            Ok(v) => v,
            Err(e) => { e.print_and_set_sys_last_vars(py); return Err(()) },
        };

        let locals = [("pyapi", pyapi)].into_py_dict(py);
        let eval_result: PyResult<&PyAny> = py.eval("pyapi.{}(**kwargs)", None, Some(&locals));

        let wrapped_obj: &PyAny = match eval_result {
            Ok(v) => v,
            Err(e) => { e.print_and_set_sys_last_vars(py); return Err(()) },
        };

        let unwrapped_result: PyResult<T> = wrapped_obj.extract();

        match unwrapped_result {
            Ok(v) => return Ok(v.clone()),
            Err(e) => return Err(()),
        };
    })
}

当我尝试编译时,出现以下错误:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'p` due to conflicting requirements
   --> src\bin\launch.rs:89:30
    |
89  |         let eval_result = py.eval("pyapi.{}(**kwargs)", None, Some(&locals));
    |                              ^^^^
    |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the body at 82:22...
   --> src\bin\launch.rs:82:22
    |
82  |       Python::with_gil(|py| {
    |  ______________________^
83  | |         let pyapi = match py.import("pyapi") {
84  | |             Ok(v) => v,
85  | |             Err(e) => { e.print_and_set_sys_last_vars(py); return Err(()) },
...   |
101 | |         };
102 | |     })
    | |_____^
note: ...so that the types are compatible
   --> src\bin\launch.rs:89:30
    |
89  |         let eval_result = py.eval("pyapi.{}(**kwargs)", None, Some(&locals));
    |                              ^^^^
    = note: expected `pyo3::Python<'_>`
               found `pyo3::Python<'_>`
note: but, the lifetime must be valid for the lifetime `'a` as defined on the function body at 81:15...
   --> src\bin\launch.rs:81:15
    |
81  | fn run_python<'a, T: FromPyObject<'a> + Clone>(func_name: &str) -> Result<T, ()> {
    |               ^^
note: ...so that the types are compatible
   --> src\bin\launch.rs:96:57
    |
96  |         let unwrapped_result: PyResult<T> = wrapped_obj.extract();
    |                                                         ^^^^^^^
    = note: expected `pyo3::FromPyObject<'_>`
               found `pyo3::FromPyObject<'a>`

我是 Rust 的新手,可能正在做一些愚蠢的事情(很可能是 X/Y 问题)。我怎样才能从 py.eval 中得到一个没有与 python 解释器相关的生命周期的值(这就是我假设在这里发生的事情)?

Ry 建议的修复似乎很有效。使用 for<'p> 可以让编译器推迟对生命周期的评估,直到在处理调用 .extract() 的代码时需要它。生命周期 'p 不需要在函数的通用参数列表中指定。此外,不需要 Clone 绑定。

fn run_python<T>(func_name: &str) -> Result<T, ()>
where
    T: for<'p> FromPyObject<'p>,
{
    let guard = Python::acquire_gil();
    let py    = guard.python();    
    match || -> _ { // try...
        let pyapi  = py.import("pyapi")?; // throw...
        let locals = [("pyapi", pyapi)].into_py_dict(py);            
        py.eval(&format!("pyapi.{}(**kwargs)", func_name), 
                None, 
                Some(&locals))?.extract()
    }() { // catch...
        Err(e) => {
            // Error handling specific to Pyo3.
            e.print_and_set_sys_last_vars(py);
            // Return the error type spec'd in the fn signature.
            Err(())
        },
        Ok(obj) => Ok(obj),
    }    
}

此方法的唯一限制是 .extract() 转换到的类型需要不依赖于它从中转换的类型的生命周期。例如,如果 run_python() 可以 return 字符串列表,这是可能的:

let values: Vec<String> = run_python("make_string_list").unwrap(); // OK.

但是这会产生与生命周期相关的编译器错误,尽管 .extract() 能够在正确的条件下产生这种类型:

let values: Vec<&str> = run_python("make_string_list").unwrap(); // Error.

如果 run_python() 需要能够生成依赖于生命周期的值,那么一个解决方案可能是让调用者获取 GIL,并传入一个 Python 实例。该函数可能如下所示:

fn run_python<'p, T>(func_name: &str, py: Python<'p>) -> PyResult<T>
where
    T: FromPyObject<'p>,
{
    let pyapi  = py.import("pyapi")?;
    let locals = [("pyapi", pyapi)].into_py_dict(py);            
    py.eval(&format!("pyapi.{}(**kwargs)", func_name), 
            None, 
            Some(&locals))?.extract()
}

在我意识到使用 for<> 的建议是最佳选择之前,我开始写另一个答案。我假设 return 值对 GIL 有一定的依赖性,但是 .extract() returns 类型不依赖于 GIL。

在之前的回答中,我提出了一些方法来处理 Python 需要在 GIL 生命周期之外保留的对象。这涉及使用 .to_object(py) 将 GIL 依赖类型转换为 GIL 独立类型,并在需要时使用 .cast_as::<PyType>(py) 等方法再次返回。