From f7537909a123f682355e5a61a2ab499637312d55 Mon Sep 17 00:00:00 2001 From: David Hewitt <1939362+davidhewitt@users.noreply.github.com> Date: Sun, 21 Aug 2022 18:49:06 +0100 Subject: [PATCH] guide: note existence of PyFunction::new_closure --- guide/src/function.md | 19 ++++++------------- guide/src/function/error_handling.md | 4 ++-- 2 files changed, 8 insertions(+), 15 deletions(-) diff --git a/guide/src/function.md b/guide/src/function.md index 008c9200c97..a370254d756 100644 --- a/guide/src/function.md +++ b/guide/src/function.md @@ -197,12 +197,6 @@ Docstring: This function adds two unsigned 64-bit integers. 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 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 -If you have a static function, you can expose it with `#[pyfunction]` and use [`wrap_pyfunction!`] -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`. -(A long-term solution will be a special container similar to wasm-bindgen's `Closure`). You can -then use a `#[pyclass]` struct with that container as a field as a way to pass the function over -the FFI barrier. You can even make that class callable with `__call__` so it looks like a function -in Python code. +The ways to convert a Rust function into a Python object vary depending on the function: + +- Named functions, e.g. `fn foo()`: add `#[pyfunction]` and then use [`wrap_pyfunction!`] to get the corresponding [`PyCFunction`]. +- Anonymous functions (or closures), e.g. `foo: fn()` either: + - use a `#[pyclass]` struct which stores the function as a field and implement `__call__` to call the stored function. + - use `PyFunction::new_closure` to create an object directly from the function. [`PyAny::is_callable`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#tymethod.is_callable [`PyAny::call`]: {{#PYO3_DOCS_URL}}/pyo3/struct.PyAny.html#tymethod.call diff --git a/guide/src/function/error_handling.md b/guide/src/function/error_handling.md index 6e722568c9f..1dd4ac9d106 100644 --- a/guide/src/function/error_handling.md +++ b/guide/src/function/error_handling.md @@ -169,14 +169,14 @@ fn parse_int(s: String) -> PyResult { 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 for PyErr` (or `PyErrArguments`), as well as `From` 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. To detail the newtype strategy a little further, the key trick is to return `Result` from the `#[pyfunction]`. This means that PyO3 will make use of `From 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`: +The following example demonstrates this for some imaginary third-party crate `some_crate` with a function `get_x` returning `Result`: ```rust # mod some_crate {