如何更改一个值,使其在 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
--> srcbinlaunch.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...
--> srcbinlaunch.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
--> srcbinlaunch.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...
--> srcbinlaunch.rs:81:15
|
81  | fn run_python<'a, T: FromPyObject<'a> + Clone>(func_name: &str) -> Result<T, ()> {
|               ^^
note: ...so that the types are compatible
--> srcbinlaunch.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()可以返回字符串列表,这是可能的:

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<>的建议是最好的选择之前,我开始写另一个答案。我假设返回值有一些依赖于GIL,但.extract()返回类型不依赖于GIL。

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

最新更新