Return 引用 PyO3 中的成员字段

Return reference to member field in PyO3

假设我有一个这样的 Rust 结构

struct X{...}

struct Y{
   x:X
}

我希望能够编写 python 通过 Y

访问 X 的代码
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()}
   }
}

但是,这需要 clone()。我宁愿return参考。我当然知道,如果 Xpyclass,那么我可以很容易地 return PyRef 到它。但问题是 XY 来自 Rust 库,我不能随意添加 #[pyclass] 到它们。

如果不对界面进行一些调整,我认为您所说的是不可能的:

您的 XWrapper 拥有 x 而您的 Y 也拥有其 x。这意味着创建 XWrapper 将始终涉及克隆(或 new)。

我们能否更改 XWrapper,使其仅包含对 x 的引用?不是真的,因为这需要给 XWrapper 一个生命周期注解,而 PyO3 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();
   }
}

对于 Demeter 法则的严格拥护者来说,这也是一个受欢迎的景象 ;)

我正在想其他聪明的方法。根据 y 本身的方法如何访问和修改 y.x 的一些细节,可能可以将字段 x: XWrapper 添加到 YWrapper。然后在创建 YWrapper 时创建 XWrapper(包括 y.x 的克隆)一次,从那时起,您可以 return 引用 XWrapper你的 pub fn x。当然,当 x 通过 y...

的方法频繁更改和更新时,这会变得更加麻烦

在某种程度上,这证明了 Python 的引用计数对象模型和 Rust 的所有权对象模型之间的冲突。 Rust 强制你不能随意乱用对象,除非你是它们的所有者。