【问题标题】:Return reference to member field in PyO3返回对 PyO3 中成员字段的引用
【发布时间】:2021-10-21 16:05:49
【问题描述】:

假设我有一个像这样的 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()}
   }
}

但是,这需要clone()。我宁愿返回参考。当然我知道如果Xpyclass,那么我可以轻松地将PyRef 返回给它。但问题是 XY 来自 Rust 库,我不能傻傻地将 #[pyclass] 添加到它们。

【问题讨论】:

  • 但是如果你想从 Python 调用 x 上的方法,它必须是 pyclass,不是吗?
  • 没关系,当然这就是您使用包装器的原因。所以现在的问题是,如果可能的话,如何避免克隆。

标签: python rust pyo3


【解决方案1】:

如果不重新调整界面,我认为你所说的是不可能的:

你的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();
   }
}

这对于德墨忒耳法则的严格追随者来说也是一个受欢迎的景象;)

我正在尝试其他聪明的方法。根据y.x 本身的方法如何访问和修改y.x 的一些细节,可以将字段x: XWrapper 添加到YWrapper。然后在创建YWrapper 时创建一次XWrapper(包括y.x 的克隆),从那时起,您可以在pub fn x 中返回对该XWrapper 的引用。当然,当x 通过y 的方法频繁更改和更新时,这变得更加麻烦......

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-03
    相关资源
    最近更新 更多