guide: note existence of PyFunction::new_closure

This commit is contained in:
David Hewitt 2022-08-21 18:49:06 +01:00
parent 86a11164e0
commit f7537909a1
2 changed files with 8 additions and 15 deletions

View File

@ -197,12 +197,6 @@ Docstring: This function adds two unsigned 64-bit integers.
Type: builtin_function_or_method Type: builtin_function_or_method
``` ```
### Closures
Currently, there are no conversions between `Fn`s in Rust and callables in Python. This would
definitely be possible and very useful, so contributions are welcome. In the meantime, you can do
the following:
### Calling Python functions in Rust ### Calling Python functions in Rust
You can pass Python `def`'d functions and built-in functions to Rust functions [`PyFunction`] You can pass Python `def`'d functions and built-in functions to Rust functions [`PyFunction`]
@ -217,13 +211,12 @@ with only positional args.
### Calling Rust functions in Python ### Calling Rust functions in Python
If you have a static function, you can expose it with `#[pyfunction]` and use [`wrap_pyfunction!`] The ways to convert a Rust function into a Python object vary depending on the function:
to get the corresponding [`PyCFunction`]. For dynamic functions, e.g. lambdas and functions that
were passed as arguments, you must put them in some kind of owned container, e.g. a `Box`. - Named functions, e.g. `fn foo()`: add `#[pyfunction]` and then use [`wrap_pyfunction!`] to get the corresponding [`PyCFunction`].
(A long-term solution will be a special container similar to wasm-bindgen's `Closure`). You can - Anonymous functions (or closures), e.g. `foo: fn()` either:
then use a `#[pyclass]` struct with that container as a field as a way to pass the function over - use a `#[pyclass]` struct which stores the function as a field and implement `__call__` to call the stored function.
the FFI barrier. You can even make that class callable with `__call__` so it looks like a function - use `PyFunction::new_closure` to create an object directly from the function.
in Python code.
[`PyAny::is_callable`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#tymethod.is_callable [`PyAny::is_callable`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#tymethod.is_callable
[`PyAny::call`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#tymethod.call [`PyAny::call`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#tymethod.call

View File

@ -169,14 +169,14 @@ fn parse_int(s: String) -> PyResult<usize> {
The Rust compiler will not permit implementation of traits for types outside of the crate where the type is defined. (This is known as the "orphan rule".) The Rust compiler will not permit implementation of traits for types outside of the crate where the type is defined. (This is known as the "orphan rule".)
Given a type `OtherError` which is defined in thirdparty code, there are two main strategies available to integrate it with PyO3: Given a type `OtherError` which is defined in third-party code, there are two main strategies available to integrate it with PyO3:
- Create a newtype wrapper, e.g. `MyOtherError`. Then implement `From<MyOtherError> for PyErr` (or `PyErrArguments`), as well as `From<OtherError>` for `MyOtherError`. - Create a newtype wrapper, e.g. `MyOtherError`. Then implement `From<MyOtherError> for PyErr` (or `PyErrArguments`), as well as `From<OtherError>` for `MyOtherError`.
- Use Rust's Result combinators such as `map_err` to write code freely to convert `OtherError` into whatever is needed. This requires boilerplate at every usage however gives unlimited flexibility. - Use Rust's Result combinators such as `map_err` to write code freely to convert `OtherError` into whatever is needed. This requires boilerplate at every usage however gives unlimited flexibility.
To detail the newtype strategy a little further, the key trick is to return `Result<T, MyOtherError>` from the `#[pyfunction]`. This means that PyO3 will make use of `From<MyOtherError> for PyErr` to create Python exceptions while the `#[pyfunction]` implementation can use `?` to convert `OtherError` to `MyOtherError` automatically. To detail the newtype strategy a little further, the key trick is to return `Result<T, MyOtherError>` from the `#[pyfunction]`. This means that PyO3 will make use of `From<MyOtherError> for PyErr` to create Python exceptions while the `#[pyfunction]` implementation can use `?` to convert `OtherError` to `MyOtherError` automatically.
The following example demonstrates this for some imaginary thirdparty crate `some_crate` with a function `get_x` returning `Result<i32, OtherError>`: The following example demonstrates this for some imaginary third-party crate `some_crate` with a function `get_x` returning `Result<i32, OtherError>`:
```rust ```rust
# mod some_crate { # mod some_crate {