2

我有一个 Rust 库,我希望能够从 Python 中使用它。所以我可以使用 PyO3 进行绑定。

假设我在 Rust 中有以下库:

pub trait Animal {
    fn make_sound();
}

pub struct Dog {}

impl Animal for Dog {
    fn make_sound() {
       println!("whoof whoof");
    }
}

pub struct Cat {}

impl Animal for Cat {
    fn make_sound() {
       println!("miaauwwww");
    }
}

pub fn make_three_sounds(&impl Animal) {
    animal.make_sound();
    animal.make_sound();
    animal.make_sound();
}

该库的用户可以使用结构 Dog 和 Cat 以及函数 make_three_sounds。但他们也可以使用特性 Animal 定义自己的动物,然后将其传递给函数 make_three_sounds。

现在,假设我希望从 Python 中使用这个库。所以我可以使用 PyO3 来制作 Rust -> Python 绑定。然而,问题是在 Python 中,这会以不同的方式实现。您将拥有一个可以扩展的类,而不是 trait。所以在 Python 中,这个库看起来像这样:

Class Animal:
    def make_sound():
        raise Error("Unimplemented abstract method")

class Dog(Animal):
    def make_sound():
        print("woaggh woafgg")

class Cat(Animal):
    def make_sound():
        print("miaaoww")

def make_three_sounds(a: Animal):
    a.make_sound()
    a.make_sound()
    a.make_sound()

为了创建一个 Dog 和 Cat 类,我只需要在 Dog 和 Cat 结构体上方使用“#[pyclass]”和“#[pymethods]”。问题在于创建一个可以扩展的 Animal 类,然后传递给函数 make_three_sounds。为了创建 Animal 类,我可以创建一个使用 Animal 特征的结构,然后在其上方添加“#[pyclass]”。

但问题是:当 Python 库的用户扩展该类(Animal)然后将其传递给 make_three_sounds 时,它将无法工作,因为 make_three_sounds 必须接受一个实现 trait Animal 的结构,而 Python 对象没有实现它特征(尽管它是扩展实现它的类/结构的类的对象)。我可以制作一个make_three_sounds接受 PyAny 对象的函数包装器,但是我以后如何将该对象传递给实际make_three_sounds函数?

4

1 回答 1

0

我可以看到的一种解决方案如下。

我创建了一个函数make_three_sounds(PyAny a),它将成为 Rustmake_three_sounds(&impl Animal a)函数的包装器。该包装器将接受 PyAny 类型的对象。稍后我将执行以下操作。我将有一个特殊的结构 PyAnimal 实现特征 Animal。该特征将具有一个名为 的属性python_object。Python 中的 PyAny 对象(表示扩展 Animal 的类的对象)稍后将分配给该属性python_object。PyAnimal 必须实现 trait 方法,并且该方法的实现将调用该方法python_object(PyAny 结构有一个 callmethod 方法,可以调用该对象的任何方法)。然后将 PyAnimal 对象传递给make_three_sounds(&impl Animal a)函数。所以 PyAnimal 的实现或多或少如下:

struct PyAnimal {
    python_object: PyAny
}

impl Animal for PyAnimal {
   fn make_sound() {
       python_object.call_method('make_sound');
   }
}
于 2020-09-27T11:45:50.193 回答