【发布时间】:2019-03-03 10:55:44
【问题描述】:
我正在为我的库编写一个 Rust 后端,我需要在 pyo3 中实现以下功能的等效项:
def f(x):
return x
这应该返回与输入相同的对象,并且获得返回值的函数应该持有对输入的新引用。如果我在 C API 中写这个,我会写成:
PyObject * f(PyObject * x) {
Py_XINCREF(x);
return x;
}
在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)
}
除此之外,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`
我可能有办法手动使用_py参数增加引用计数并使用生命周期注解让编译器高兴,但我的印象是pyo3打算管理使用对象生命周期的引用计数自身。
编写此函数的正确方法是什么?我应该尝试将其包装在 Py 容器中吗?
【问题讨论】:
-
@Shepmaster 我认为您的编辑使标题不太准确。引用计数增加的事实不是我想在 Rust 版本中管理或考虑的事情。如果 pyo3 做了其他相当于增加引用计数的事情,那很好。
-
@Shepmaster 这并不是一个真正的硬性要求。如果
pyo3保留对创建的任何 Python 对象的单个引用,然后使用 Rust 对象生命周期或其他方式管理是否释放该引用,那很好。我真的不知道也不关心,只要我从 pyo3 的角度使用正确的生命周期管理。 -
x的生命周期和返回值不一样——你为什么这么说? Lifetime elision 使它们相同。您可以明确地注释它们以“证明”它:fn f<'a>(_py: Python, x: &'a PyObject) -> PyResult<&'a PyObject>. -
@Shepmaster 这样做似乎无论如何都会导致编译器错误,因为
pyfunc没有保留生命周期参数。 -
至于生命周期问题,这是编译器错误的要点——这可能是@E_net4 所说的问题,但我认为 Python 引用计数的重点是输入的生命周期并且输出应该是断开的(因此引用计数),所以即使 Rust 语义说它具有正确的生命周期声明,这不是函数的预期使用方式。