rust 使用带PyO3的盒子

yhived7q  于 2023-01-21  发布在  其他
关注(0)|答案(1)|浏览(218)

我在Rust中有一个结构体,它的工作原理类似于链表,我想将它公开给Python,这个结构体有一个parent字段,它是对一个父结构体的引用,这个父结构体是一个相同类型的结构体,我需要将它 Package 在一个Box中,因为Rust抱怨如果我不这样做,它需要间接寻址,但是PyO3给出了下面的错误:

required for `Box<ListNode>` to implement `pyo3::FromPyObject<'_>`
required for `Box<ListNode>` to implement `PyFunctionArgument<'_, '_>`

该结构体的简化版本如下所示:

#[pyclass]
#[derive(Clone)]
pub struct ListNode {
    pub parent: Option<Box<ListNode>>,
}
#[pymethods]
impl ListNode {
    #[new]
    pub fn new(parent: Option<Box<ListNode>>) -> ListNode {
        ListNode { parent }
    }
}

我需要做什么来实现BoxFromPyObject?或者有更正确的方法来解决这个问题吗?看起来只要Rust中有Box,无论Box的内容是什么,错误都会发生。
编辑:cargo check的完整错误输出如下:

error[E0277]: the trait bound `Box<ListNode>: PyClass` is not satisfied
   --> src/ListNode.rs:14:1
    |
14  | #[pymethods]
    | ^^^^^^^^^^^^ the trait `PyClass` is not implemented for `Box<ListNode>`
...
17  |     pub fn new(parent: Option<Box<ListNode>>) -> ListNode {
    |                        ------ required by a bound introduced by this call
    |
    = note: required for `Box<ListNode>` to implement `pyo3::FromPyObject<'_>`
    = note: required for `Box<ListNode>` to implement `PyFunctionArgument<'_, '_>`
note: required by a bound in `extract_optional_argument`
   --> /me/.cargo/registry/src/github.com-1ecc6299db9ec823/pyo3-0.17.3/src/impl_/extract_argument.rs:104:8
    |
104 |     T: PyFunctionArgument<'a, 'py>,
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `extract_optional_argument`

error[E0277]: the trait bound `Box<ListNode>: PyClass` is not satisfied
  --> src/ListNode.rs:17:24
   |
17 |     pub fn new(parent: Option<Box<ListNode>>) -> ListNode {
   |                        ^^^^^^ the trait `PyClass` is not implemented for `Box<ListNode>`
   |
   = note: required for `Box<ListNode>` to implement `pyo3::FromPyObject<'_>`
   = note: required for `Box<ListNode>` to implement `PyFunctionArgument<'_, '_>`
ycl3bljg

ycl3bljg1#

您看到的错误消息与'FromPyObject' trait有关,该trait是将Python对象转换为Rust结构所必需的。PyO 3无法为'Box'自动实现此trait,因为它不知道如何处理'Box'表示的间接寻址。
解决这个问题的一个方法是为Box手动实现FromPyObject trait。这可以通过定义一个新的结构来 Package Box并为它实现FromPyObject trait来实现。下面是一个示例:

use pyo3::prelude::*;
use pyo3::types::PyAny;

struct ListNodeBox {
    inner: Box<ListNode>,
}

impl FromPyObject<'_> for ListNodeBox {
    fn from_py_object(py: Python, obj: &PyAny) -> PyResult<Self> {
        let parent = obj.getattr("parent")?;
        let parent = parent.extract::<Option<Box<ListNodeBox>>>()?;
        Ok(ListNodeBox {
            inner: Box::new(ListNode::new(parent)),
        })
    }
}

#[pyclass]
#[derive(Clone)]
pub struct ListNode {
    pub parent: Option<ListNodeBox>,
}

//这样,您将创建一个新结构ListNodeBox,它 Package Box并实现FromPyObject特性。然后,您可以在ListNode结构的定义中使用此结构,而不是直接使用Box。
解决这个问题的另一种方法是将结构体中的父字段更改为'Py'而不是'Option','Py'是来自pyo 3库的 Package 器结构体,提供类似智能指针的行为,它负责引用计数和向Python的转换。
请注意,这些解决方案只是示例,可能需要根据您的特定用例进行其他调整。测试代码并确保正确转换结构体以及正确设置父字段也很重要。

相关问题