我想为我的库编写一个Rust后端,我需要在以下函数中实现以下函数的等价物pyo3:
def f(x):
return x
Run Code Online (Sandbox Code Playgroud)
这应该返回与输入相同的对象,获取返回值的函数应该包含对输入的新引用.如果我在C API中写这个,我会把它写成:
PyObject * f(PyObject * x) {
Py_XINCREF(x);
return x;
}
Run Code Online (Sandbox Code Playgroud)
在PyO3,我觉得相当混乱导航之间的差异PyObject,PyObjectRef,&PyObject,Py<PyObject>,Py<&PyObject>.
这个函数最天真的版本是:
extern crate pyo3;
use pyo3::prelude::*;
#[pyfunction]
pub fn f(_py: Python, x: &PyObject) -> PyResult<&PyObject> {
Ok(x)
}
Run Code Online (Sandbox Code Playgroud)
除其他外,生命周期x和返回值不一样,加上我认为没有机会pyo3增加引用计数x,实际上编译器似乎同意我的意见:
error[E0106]: missing lifetime specifier
--> src/lib.rs:4:49
|
4 | pub fn f(_py: Python, x: &PyObject) -> PyResult<&PyObject> {
| ^ expected lifetime parameter
|
= help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `_py` or `x`
Run Code Online (Sandbox Code Playgroud)
有可能是一个办法,我手动增加引用计数使用的_py参数和使用的寿命注释,使编译器高兴,但我的印象是pyo3打算来管理引用计数本身使用对象的寿命.
编写此函数的正确方法是什么?我应该尝试将其包装在Py容器中吗?
A PyObject是原始指针的简单包装:
pub struct PyObject(*mut ffi::PyObject);
Run Code Online (Sandbox Code Playgroud)
它有多个创建函数,每个函数对应于我们可能从Python获得的不同类型的指针.其中一些,例如from_borrowed_ptr,调用Py_INCREF传入的指针.
因此,似乎我们可以接受a PyObject,只要它是以"正确"的方式创建的.
如果我们扩展此代码:
#[pyfunction]
pub fn example(_py: Python, x: PyObject) -> PyObject {
x
}
Run Code Online (Sandbox Code Playgroud)
我们可以看到调用我们函数的代码段:
let mut _iter = _output.iter();
::pyo3::ObjectProtocol::extract(_iter.next().unwrap().unwrap()).and_then(
|arg1| {
::pyo3::ReturnTypeIntoPyResult::return_type_into_py_result(example(
_py, arg1,
))
},
)
Run Code Online (Sandbox Code Playgroud)
我们的参数是通过调用来创建的ObjectProtocol::extract,而调用又会调用FromPyObject::extract.这是通过调用实现的PyObjectfrom_borrowed_ptr.
因此,使用bare PyObject作为参数类型将正确地增加引用计数.
同样,当PyObject在Rust中删除a时,它将自动减少引用计数.当它返回到Python时,将转移所有权,并由Python代码来适当地更新引用计数.
所有调查都是从master分支提交ed273982,对应于v0.5.0-alpha.1.
| 归档时间: |
|
| 查看次数: |
446 次 |
| 最近记录: |