pyo3/guide/src/rust-cpython.md

92 lines
2.4 KiB
Markdown
Raw Normal View History

2018-05-17 21:59:11 +00:00
# Appendix: pyo3 and rust-cpython
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.
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 use proc macros and spezialization. Pyo3 also doesn't change your struct and functions so you can still use them as normal rust functions. The disadvantage is that proc macros and spezialization currently only work on nightly.
**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
#![feature(proc_macro, specialization)]
extern crate pyo3;
use pyo3::prelude::*;
use pyo3::py::{class, methods};
#[class]
struct MyClass {
num: u32,
}
#[methods]
impl MyClass {
#[new]
fn __new__(obj: &PyRawObject, num: u32) -> PyResult<()> {
obj.init(|token| {
MyClass {
num,
}
})
}
fn half(&self) -> PyResult<u32> {
Ok(self.num / 2)
}
}
```
## Ownership and lifetimes
All objects are owned by pyo3 library and all apis available with references, while in rust-cpython, you own python objects.
Here is example of PyList api:
**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 object, all reference have the Gil lifetime. So the python object is not required, and it is safe to have functions like `fn py<'p>(&'p self) -> Python<'p> {}`.
## Error handling
rust-cpython requires a `Python` parameter for `PyErr`, so error handling ergonomics is pretty bad. It is not possible to use `?` with rust errors.
`pyo3` on other hand does not require `Python` for `PyErr`, it is only required if you want to raise an exception in python with the `PyErr::restore()` method. Due to the `std::convert::From<Err> for PyErr` trait `?` is supported automatically.