rust 返回对PyO3中成员字段的引用

ujv3wf0j  于 2022-11-12  发布在  其他
关注(0)|答案(2)|浏览(171)

假设我有一个Rust结构体

struct X{...}

struct Y{
   x:X
}

我希望能够编写通过Y访问X的python代码

y = Y()
y.x.some_method()

在PyO3中实现它的最佳方式是什么?


# [pyclass]

struct XWrapper{
   x:X
}

# [pyclass]

struct YWrapper{
   y:Y
}

# [pymethods]

impl YWrapper{
   #[getter]
   pub fn x(&self)->XWrapper{
      XWrapper{x:self.y.clone()}
   }
}

当然,我知道如果Xpyclass,那么我可以很容易地将PyRef返回给它。但问题是,XY来自Rust库,我不可能无凭无据地将#[pyclass]添加到它们。

8dtrkrch

8dtrkrch1#

我不认为你所说的是可能的没有一些重新调整的接口:
你的XWrapper * 拥有 * x,你的Y也拥有它的x。这意味着创建一个XWrapper将总是涉及一个克隆(或new)。
我们可以修改XWrapper,使其只包含对x的引用吗?不太可能,因为这需要给XWrapper一个生存期注解,而PyO 3 afaik不允许pyclasses带有生存期注解。这是有意义的,因为将一个对象传递给python会将其放在python堆中,此时rust将失去对该对象的控制。
那我们能做些什么?
一些想法:你 * 真的 * 需要把y的组成结构公开给python模块吗?仅仅因为Rust中的组织方式并不意味着Python中的组织方式也是如此。你的YWrapper可以提供方法给python接口,在后台把请求转发给x示例:


# [pymethods]

impl YWrapper{
   pub fn some_method(&self) {
     self.y.x.some_method();
   }
}

这也将是一个受欢迎的景象严格遵守得墨忒耳的法律;)
我正在尝试其他更聪明的方法,根据y本身的方法如何访问和修改y.x的一些细节,可能会向YWrapper添加一个字段x: XWrapper。(包括y.x的克隆)一旦当创建YWrapper时,并且从那时起,您可以在pub fn x中返回对该XWrapper的引用。当然,当x通过y的方法频繁地更改和更新时,这会变得更加麻烦...
在某种程度上,这表明了Python的引用计数对象模型和Rust的所有权对象模型之间的冲突。Rust强制你不能随意地弄乱对象,除非你是它们的所有者。

2hh7jdfx

2hh7jdfx2#

共享对象并返回它们或改变它们确实是可能的。无论如何,就像Python一样。Lagerbaer的建议是可行的,它实际上非常适合小代码。然而,如果方法的数量增加,将需要大量的重复和样板(更糟糕的是,每次增加嵌套的深度时都会折叠)。
我不知道这是否是我们应该做的事情。但据我所知,做这件事的方法是使用Py。上帝希望我有一个习惯,在实验前彻底阅读文档。
在https://docs.rs/pyo3/latest/pyo3/#the-gil-independent-types的MAIN PAGE中的doc表示:当用Py Package 时<...>,就像Py或Py一样,Python对象不再有有限的生命周期,这使得它们更容易存储在结构体中并在函数之间传递。然而,如果没有Python〈'py〉标记,你就不能用它们做很多事情,你需要重新获得GIL。
Py是“对Python堆上分配的对象的独立于GIL的引用。”https://docs.rs/pyo3/latest/pyo3/prelude/struct.Py.html
换句话说,要返回pyclass对象,我们需要像Py<pyclass_struct_name>那样 Package 它。
你的例子太复杂了,老实说我不明白你想做什么,但这里有一个更适合我自己的例子的替代版本。因为这基本上是谷歌弹出的唯一结果之一,我认为它适合粘贴在这里,即使它不是一个确切的回应提供了上述的例子。
所以我们开始吧...
假设我们有一个Rust结构体X,我们不能像你提到的那样修改库,我们需要一个XWrapper(我们称之为PyXpyclass来保存它。
因此,我们在此定义它们:
第一个
然后对于用法,我们所要做的就是用GIL锁初始化对象(假设在XWrapper的init中),然后为它定义一个getter。* 这里的重要注意事项是,您在它上调用clone_ref,并且不要返回该对象 *。
这基本上是一个嵌套的类系统,嵌套的对象是不可变的(具有内部可变性),所以这也是嵌套代码的一种奇妙的方式。
在下面的示例中,我将所需的X用作另一个名为Api的 Package 器中的PyX


# [pyclass]

struct Api {
    x: PyX,
}

# [pymethods]

impl Api {
    #[new]
    fn __new__() -> PyResult<Self> {
        Python::with_gil(|py| {
            Ok(Self {
                x: Py::new(
                    py,
                    PyX::new(),
                ),
            })
        }
    }

    #[getter(x)]
    fn x(&mut self, py: Python) -> Py<Network> {
        self.x.clone_ref(py)
    }
}

相关问题