pyo3/guide/src/rust_cpython.md

83 lines
2.1 KiB
Markdown
Raw Normal View History

2020-09-05 13:44:39 +00:00
# PyO3 and rust-cpython
2018-05-17 21:59:11 +00:00
2020-09-05 13:44:39 +00:00
PyO3 began as fork of [rust-cpython](https://github.com/dgrunwald/rust-cpython) when rust-cpython wasn't maintained. Over time PyO3 has become fundamentally different from rust-cpython.
2018-05-17 21:59:11 +00:00
## Macros
While rust-cpython has a `macro_rules!` based dsl for declaring modules and classes, PyO3 uses proc macros. PyO3 also doesn't change your struct and functions so you can still use them as normal Rust functions.
2018-05-17 21:59:11 +00:00
**rust-cpython**
```rust,ignore
py_class!(class MyClass |py| {
data number: i32;
def __new__(_cls, arg: i32) -> PyResult<MyClass> {
MyClass::create_instance(py, arg)
}
def half(&self) -> PyResult<i32> {
Ok(self.number(py) / 2)
}
});
```
**pyo3**
```rust
use pyo3::prelude::*;
#[pyclass]
2018-05-17 21:59:11 +00:00
struct MyClass {
num: u32,
}
#[pymethods]
2018-05-17 21:59:11 +00:00
impl MyClass {
#[new]
fn new(num: u32) -> Self {
MyClass { num }
2018-05-17 21:59:11 +00:00
}
fn half(&self) -> PyResult<u32> {
Ok(self.num / 2)
}
}
```
## Ownership and lifetimes
While in rust-cpython you always own python objects, PyO3 allows efficient *borrowed objects*
and most APIs are available with references.
2018-05-17 21:59:11 +00:00
Here is an example of the PyList API:
2018-05-17 21:59:11 +00:00
**rust-cpython**
```rust,ignore
impl PyList {
2022-03-23 07:07:28 +00:00
fn new(py: Python<'_>) -> PyList {...}
2018-05-17 21:59:11 +00:00
2022-03-23 07:07:28 +00:00
fn get_item(&self, py: Python<'_>, index: isize) -> PyObject {...}
2018-05-17 21:59:11 +00:00
}
```
**pyo3**
```rust,ignore
impl PyList {
2022-03-23 07:07:28 +00:00
fn new(py: Python<'_>) -> &PyList {...}
2018-05-17 21:59:11 +00:00
fn get_item(&self, index: isize) -> &PyAny {...}
2018-05-17 21:59:11 +00:00
}
```
In PyO3, all object references are bounded by the GIL lifetime.
So the owned Python object is not required, and it is safe to have functions like `fn py<'p>(&'p self) -> Python<'p> {}`.
2018-05-17 21:59:11 +00:00
## Error handling
rust-cpython requires a `Python` parameter for constructing a `PyErr`, so error handling ergonomics is pretty bad. It is not possible to use `?` with Rust errors.
2018-05-17 21:59:11 +00:00
PyO3 on other hand does not require `Python` for constructing a `PyErr`, it is only required if you want to raise an exception in Python with the `PyErr::restore()` method. Due to various `std::convert::From<E> for PyErr` implementations for Rust standard error types `E`, propagating `?` is supported automatically.