From 64a6a02bf0b0498ec6d23d3fc13cc260bec5de03 Mon Sep 17 00:00:00 2001 From: Bartosz Telenczuk Date: Fri, 2 Feb 2024 17:16:23 +0100 Subject: [PATCH] add example for wrapping generic classes --- guide/src/class.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/guide/src/class.md b/guide/src/class.md index 37265aaa..dfe5c8ae 100644 --- a/guide/src/class.md +++ b/guide/src/class.md @@ -82,6 +82,36 @@ When you need to share ownership of data between Python and Rust, instead of usi A Rust `struct Foo` with a generic parameter `T` generates new compiled implementations each time it is used with a different concrete type for `T`. These new implementations are generated by the compiler at each usage site. This is incompatible with wrapping `Foo` in Python, where there needs to be a single compiled implementation of `Foo` which is integrated with the Python interpreter. +Currently, the best alternative is to write a macro which expands to a new #[pyclass] for each instantiation you want: + +```rust +# #![allow(dead_code)] +use pyo3::prelude::*; + +struct GenericClass { + data: T +} + +macro_rules! create_interface { + ($name: ident, $type: ident) => { + #[pyclass] + pub struct $name { + inner: GenericClass<$type>, + } + #[pymethods] + impl $name { + #[new] + pub fn new(data: $type) -> Self { + Self { inner: GenericClass { data: data } } + } + } + }; +} + +create_interface!(IntClass, i64); +create_interface!(FloatClass, String); +``` + #### Must be Send Because Python objects are freely shared between threads by the Python interpreter, there is no guarantee which thread will eventually drop the object. Therefore all types annotated with `#[pyclass]` must implement `Send` (unless annotated with [`#[pyclass(unsendable)]`](#customizing-the-class)).