pyo3/guide/src/rust_cpython.md

88 lines
2.4 KiB
Markdown
Raw Normal View History

2019-03-13 16:26:48 +00:00
# Appendix: PyO3 and rust-cpython
2018-05-17 21:59:11 +00:00
PyO3 began as fork of [rust-cpython](https://github.com/dgrunwald/rust-cpython) when rust-cpython wasn't maintained. Over the time PyO3 has become fundamentally different from rust-cpython.
2018-05-17 21:59:11 +00:00
This chapter is based on the discussion in [PyO3/pyo3#55](https://github.com/PyO3/pyo3/issues/55).
## Macros
While rust-cpython has a macro based dsl for declaring modules and classes, PyO3 uses proc macros and specialization. PyO3 also doesn't change your struct and functions so you can still use them as normal Rust functions. The disadvantage is that specialization currently only works on nightly.
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::*;
2018-09-21 21:32:48 +00:00
use pyo3::PyRawObject;
2018-05-17 21:59:11 +00:00
#[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]
2019-02-23 17:38:00 +00:00
fn new(obj: &PyRawObject, num: u32) {
obj.init({
2018-05-17 21:59:11 +00:00
MyClass {
num,
}
2019-02-23 17:38:00 +00:00
});
2018-05-17 21:59:11 +00:00
}
fn half(&self) -> PyResult<u32> {
Ok(self.num / 2)
}
}
```
## Ownership and lifetimes
All objects are owned by the PyO3 library and all APIs available with references, while in rust-cpython, you own python objects.
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 {
fn new(py: Python) -> PyList {...}
fn get_item(&self, py: Python, index: isize) -> PyObject {...}
}
```
**pyo3**
```rust,ignore
impl PyList {
fn new(py: Python) -> &PyList {...}
fn get_item(&self, index: isize) -> &PyObject {...}
}
```
Because PyO3 allows only references to Python objects, all references have 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.