Skip to content

Commit 1f82315

Browse files
committed
add second lifetime to PyRef and PyRefMut
1 parent 4e97ebb commit 1f82315

32 files changed

+662
-574
lines changed

guide/src/class.md

Lines changed: 32 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
PyO3 exposes a group of attributes powered by Rust's proc macro system for defining Python classes as Rust structs.
44

5-
The main attribute is `#[pyclass]`, which is placed upon a Rust `struct` or `enum` to generate a Python type for it. They will usually also have *one* `#[pymethods]`-annotated `impl` block for the struct, which is used to define Python methods and constants for the generated Python type. (If the [`multiple-pymethods`] feature is enabled, each `#[pyclass]` is allowed to have multiple `#[pymethods]` blocks.) `#[pymethods]` may also have implementations for Python magic methods such as `__str__`.
5+
The main attribute is `#[pyclass]`, which is placed upon a Rust `struct` or `enum` to generate a Python type for it. They will usually also have _one_ `#[pymethods]`-annotated `impl` block for the struct, which is used to define Python methods and constants for the generated Python type. (If the [`multiple-pymethods`] feature is enabled, each `#[pyclass]` is allowed to have multiple `#[pymethods]` blocks.) `#[pymethods]` may also have implementations for Python magic methods such as `__str__`.
66

77
This chapter will discuss the functionality and configuration these attributes offer. Below is a list of links to the relevant section of this chapter for each:
88

@@ -22,6 +22,7 @@ This chapter will discuss the functionality and configuration these attributes o
2222
## Defining a new class
2323

2424
To define a custom Python class, add the `#[pyclass]` attribute to a Rust struct or enum.
25+
2526
```rust
2627
# #![allow(dead_code)]
2728
use pyo3::prelude::*;
@@ -202,13 +203,14 @@ fn my_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
202203

203204
It is often useful to turn a `#[pyclass]` type `T` into a Python object and access it from Rust code. The [`Py<T>`] and [`Bound<'py, T>`] smart pointers are the ways to represent a Python object in PyO3's API. More detail can be found about them [in the Python objects](./types.md#pyo3s-smart-pointers) section of the guide.
204205

205-
Most Python objects do not offer exclusive (`&mut`) access (see the [section on Python's memory model](./python-from-rust.md#pythons-memory-model)). However, Rust structs wrapped as Python objects (called `pyclass` types) often *do* need `&mut` access. Due to the GIL, PyO3 *can* guarantee exclusive access to them.
206+
Most Python objects do not offer exclusive (`&mut`) access (see the [section on Python's memory model](./python-from-rust.md#pythons-memory-model)). However, Rust structs wrapped as Python objects (called `pyclass` types) often _do_ need `&mut` access. Due to the GIL, PyO3 _can_ guarantee exclusive access to them.
206207

207208
The Rust borrow checker cannot reason about `&mut` references once an object's ownership has been passed to the Python interpreter. This means that borrow checking is done at runtime using with a scheme very similar to `std::cell::RefCell<T>`. This is known as [interior mutability](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html).
208209

209210
Users who are familiar with `RefCell<T>` can use `Py<T>` and `Bound<'py, T>` just like `RefCell<T>`.
210211

211212
For users who are not very familiar with `RefCell<T>`, here is a reminder of Rust's rules of borrowing:
213+
212214
- At any given time, you can have either (but not both of) one mutable reference or any number of immutable references.
213215
- References can never outlast the data they refer to.
214216

@@ -309,11 +311,11 @@ Generally, `#[new]` methods have to return `T: Into<PyClassInitializer<Self>>` o
309311
For constructors that may fail, you should wrap the return type in a PyResult as well.
310312
Consult the table below to determine which type your constructor should return:
311313

312-
| | **Cannot fail** | **May fail** |
313-
|-----------------------------|---------------------------|-----------------------------------|
314-
|**No inheritance** | `T` | `PyResult<T>` |
315-
|**Inheritance(T Inherits U)**| `(T, U)` | `PyResult<(T, U)>` |
316-
|**Inheritance(General Case)**| [`PyClassInitializer<T>`] | `PyResult<PyClassInitializer<T>>` |
314+
| | **Cannot fail** | **May fail** |
315+
| ----------------------------- | ------------------------- | --------------------------------- |
316+
| **No inheritance** | `T` | `PyResult<T>` |
317+
| **Inheritance(T Inherits U)** | `(T, U)` | `PyResult<(T, U)>` |
318+
| **Inheritance(General Case)** | [`PyClassInitializer<T>`] | `PyResult<PyClassInitializer<T>>` |
317319

318320
## Inheritance
319321

@@ -323,7 +325,6 @@ Currently, only classes defined in Rust and builtins provided by PyO3 can be inh
323325
from; inheriting from other classes defined in Python is not yet supported
324326
([#991](https://github.com/PyO3/pyo3/issues/991)).
325327

326-
327328
For convenience, `(T, U)` implements `Into<PyClassInitializer<T>>` where `U` is the
328329
base class of `T`.
329330
But for a more deeply nested inheritance, you have to return `PyClassInitializer<T>`
@@ -370,7 +371,7 @@ impl SubClass {
370371
(SubClass { val2: 15 }, BaseClass::new())
371372
}
372373

373-
fn method2(self_: PyRef<'_, Self>) -> PyResult<usize> {
374+
fn method2(self_: PyRef<'_, '_, Self>) -> PyResult<usize> {
374375
let super_ = self_.as_super(); // Get &PyRef<BaseClass>
375376
super_.method1().map(|x| x * self_.val2)
376377
}
@@ -388,24 +389,24 @@ impl SubSubClass {
388389
PyClassInitializer::from(SubClass::new()).add_subclass(SubSubClass { val3: 20 })
389390
}
390391

391-
fn method3(self_: PyRef<'_, Self>) -> PyResult<usize> {
392+
fn method3(self_: PyRef<'_, '_, Self>) -> PyResult<usize> {
392393
let base = self_.as_super().as_super(); // Get &PyRef<'_, BaseClass>
393394
base.method1().map(|x| x * self_.val3)
394395
}
395396

396-
fn method4(self_: PyRef<'_, Self>) -> PyResult<usize> {
397+
fn method4(self_: PyRef<'_, '_, Self>) -> PyResult<usize> {
397398
let v = self_.val3;
398399
let super_ = self_.into_super(); // Get PyRef<'_, SubClass>
399400
SubClass::method2(super_).map(|x| x * v)
400401
}
401402

402-
fn get_values(self_: PyRef<'_, Self>) -> (usize, usize, usize) {
403+
fn get_values(self_: PyRef<'_, '_, Self>) -> (usize, usize, usize) {
403404
let val1 = self_.as_super().as_super().val1;
404405
let val2 = self_.as_super().val2;
405406
(val1, val2, self_.val3)
406407
}
407408

408-
fn double_values(mut self_: PyRefMut<'_, Self>) {
409+
fn double_values(mut self_: PyRefMut<'_, '_, Self>) {
409410
self_.as_super().as_super().val1 *= 2;
410411
self_.as_super().val2 *= 2;
411412
self_.val3 *= 2;
@@ -481,6 +482,7 @@ impl DictWithCounter {
481482
```
482483

483484
If `SubClass` does not provide a base class initialization, the compilation fails.
485+
484486
```rust,compile_fail
485487
# use pyo3::prelude::*;
486488
@@ -504,7 +506,7 @@ impl SubClass {
504506
```
505507

506508
The `__new__` constructor of a native base class is called implicitly when
507-
creating a new instance from Python. Be sure to accept arguments in the
509+
creating a new instance from Python. Be sure to accept arguments in the
508510
`#[new]` method that you want the base class to get, even if they are not used
509511
in that `fn`:
510512

@@ -542,6 +544,7 @@ initial items, such as `MyDict(item_sequence)` or `MyDict(a=1, b=2)`.
542544
## Object properties
543545

544546
PyO3 supports two ways to add properties to your `#[pyclass]`:
547+
545548
- For simple struct fields with no side effects, a `#[pyo3(get, set)]` attribute can be added directly to the field definition in the `#[pyclass]`.
546549
- For properties which require computation you can define `#[getter]` and `#[setter]` functions in the [`#[pymethods]`](#instance-methods) block.
547550

@@ -566,6 +569,7 @@ The above would make the `num` field available for reading and writing as a `sel
566569
Properties can be readonly or writeonly by using just `#[pyo3(get)]` or `#[pyo3(set)]` respectively.
567570

568571
To use these annotations, your field type must implement some conversion traits:
572+
569573
- For `get` the field type must implement both `IntoPy<PyObject>` and `Clone`.
570574
- For `set` the field type must implement `FromPyObject`.
571575

@@ -733,15 +737,16 @@ impl MyClass {
733737

734738
Declares a class method callable from Python.
735739

736-
* The first parameter is the type object of the class on which the method is called.
740+
- The first parameter is the type object of the class on which the method is called.
737741
This may be the type object of a derived class.
738-
* The first parameter implicitly has type `&Bound<'_, PyType>`.
739-
* For details on `parameter-list`, see the documentation of `Method arguments` section.
740-
* The return type must be `PyResult<T>` or `T` for some `T` that implements `IntoPy<PyObject>`.
742+
- The first parameter implicitly has type `&Bound<'_, PyType>`.
743+
- For details on `parameter-list`, see the documentation of `Method arguments` section.
744+
- The return type must be `PyResult<T>` or `T` for some `T` that implements `IntoPy<PyObject>`.
741745

742746
### Constructors which accept a class argument
743747

744748
To create a constructor which takes a positional class argument, you can combine the `#[classmethod]` and `#[new]` modifiers:
749+
745750
```rust
746751
# #![allow(dead_code)]
747752
# use pyo3::prelude::*;
@@ -807,7 +812,7 @@ Python::with_gil(|py| {
807812
```
808813

809814
> Note: if the method has a `Result` return type and returns an `Err`, PyO3 will panic during
810-
class creation.
815+
> class creation.
811816
812817
If the class attribute is defined with `const` code only, one can also annotate associated
813818
constants:
@@ -844,7 +849,7 @@ fn increment_field(my_class: &mut MyClass) {
844849
// Take a reference wrapper when borrowing should be automatic,
845850
// but interaction with the underlying `Bound` is desired.
846851
#[pyfunction]
847-
fn print_field(my_class: PyRef<'_, MyClass>) {
852+
fn print_field(my_class: PyRef<'_, '_, MyClass>) {
848853
println!("{}", my_class.my_field);
849854
}
850855

@@ -1193,7 +1198,7 @@ Python::with_gil(|py| {
11931198
```
11941199

11951200
Ordering of enum variants is optionally added using `#[pyo3(ord)]`.
1196-
*Note: Implementation of the `PartialOrd` trait is required when passing the `ord` argument. If not implemented, a compile time error is raised.*
1201+
_Note: Implementation of the `PartialOrd` trait is required when passing the `ord` argument. If not implemented, a compile time error is raised._
11971202

11981203
```rust
11991204
# use pyo3::prelude::*;
@@ -1390,22 +1395,22 @@ impl pyo3::PyClass for MyClass {
13901395
type Frozen = pyo3::pyclass::boolean_struct::False;
13911396
}
13921397

1393-
impl<'a, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a MyClass
1398+
impl<'a, 'holder, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py> for &'holder MyClass
13941399
{
1395-
type Holder = ::std::option::Option<pyo3::PyRef<'py, MyClass>>;
1400+
type Holder = ::std::option::Option<pyo3::PyRef<'a, 'py, MyClass>>;
13961401

13971402
#[inline]
1398-
fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'a mut Self::Holder) -> pyo3::PyResult<Self> {
1403+
fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'holder mut Self::Holder) -> pyo3::PyResult<Self> {
13991404
pyo3::impl_::extract_argument::extract_pyclass_ref(obj, holder)
14001405
}
14011406
}
14021407

1403-
impl<'a, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'py> for &'a mut MyClass
1408+
impl<'a, 'holder, 'py> pyo3::impl_::extract_argument::PyFunctionArgument<'a, 'holder, 'py> for &'holder mut MyClass
14041409
{
1405-
type Holder = ::std::option::Option<pyo3::PyRefMut<'py, MyClass>>;
1410+
type Holder = ::std::option::Option<pyo3::PyRefMut<'a, 'py, MyClass>>;
14061411

14071412
#[inline]
1408-
fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'a mut Self::Holder) -> pyo3::PyResult<Self> {
1413+
fn extract(obj: &'a pyo3::Bound<'py, PyAny>, holder: &'holder mut Self::Holder) -> pyo3::PyResult<Self> {
14091414
pyo3::impl_::extract_argument::extract_pyclass_ref_mut(obj, holder)
14101415
}
14111416
}
@@ -1459,22 +1464,16 @@ impl pyo3::impl_::pyclass::PyClassImpl for MyClass {
14591464
# }
14601465
```
14611466

1462-
14631467
[`PyTypeInfo`]: {{#PYO3_DOCS_URL}}/pyo3/type_object/trait.PyTypeInfo.html
1464-
14651468
[`Py`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Py.html
14661469
[`Bound<'_, T>`]: {{#PYO3_DOCS_URL}}/pyo3/struct.Bound.html
14671470
[`PyClass`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass/trait.PyClass.html
14681471
[`PyRef`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRef.html
14691472
[`PyRefMut`]: {{#PYO3_DOCS_URL}}/pyo3/pycell/struct.PyRefMut.html
14701473
[`PyClassInitializer<T>`]: {{#PYO3_DOCS_URL}}/pyo3/pyclass_init/struct.PyClassInitializer.html
1471-
14721474
[`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
14731475
[`RefCell`]: https://doc.rust-lang.org/std/cell/struct.RefCell.html
1474-
14751476
[classattr]: https://docs.python.org/3/tutorial/classes.html#class-and-instance-variables
1476-
14771477
[`multiple-pymethods`]: features.md#multiple-pymethods
1478-
14791478
[lifetime-elision]: https://doc.rust-lang.org/reference/lifetime-elision.html
14801479
[compiler-error-e0106]: https://doc.rust-lang.org/error_codes/E0106.html

guide/src/class/numeric.md

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
At this point we have a `Number` class that we can't actually do any math on!
44

55
Before proceeding, we should think about how we want to handle overflows. There are three obvious solutions:
6+
67
- We can have infinite precision just like Python's `int`. However that would be quite boring - we'd
7-
be reinventing the wheel.
8+
be reinventing the wheel.
89
- We can raise exceptions whenever `Number` overflows, but that makes the API painful to use.
910
- We can wrap around the boundary of `i32`. This is the approach we'll take here. To do that we'll just forward to `i32`'s
10-
`wrapping_*` methods.
11+
`wrapping_*` methods.
1112

1213
### Fixing our constructor
1314

@@ -42,6 +43,7 @@ fn wrap(obj: &Bound<'_, PyAny>) -> PyResult<i32> {
4243
Ok(val as i32)
4344
}
4445
```
46+
4547
We also add documentation, via `///` comments, which are visible to Python users.
4648

4749
```rust
@@ -68,8 +70,8 @@ impl Number {
6870
}
6971
```
7072

71-
7273
With that out of the way, let's implement some operators:
74+
7375
```rust
7476
use pyo3::exceptions::{PyZeroDivisionError, PyValueError};
7577

@@ -132,7 +134,7 @@ impl Number {
132134
#
133135
#[pymethods]
134136
impl Number {
135-
fn __pos__(slf: PyRef<'_, Self>) -> PyRef<'_, Self> {
137+
fn __pos__<'a, 'py>(slf: PyRef<'a, 'py, Self>) -> PyRef<'a, 'py, Self> {
136138
slf
137139
}
138140

@@ -178,7 +180,7 @@ impl Number {
178180

179181
We do not implement the in-place operations like `__iadd__` because we do not wish to mutate `Number`.
180182
Similarly we're not interested in supporting operations with different types, so we do not implement
181-
the reflected operations like `__radd__` either.
183+
the reflected operations like `__radd__` either.
182184

183185
Now Python can use our `Number` class:
184186

@@ -405,13 +407,15 @@ function that does:
405407
unsigned long PyLong_AsUnsignedLongMask(PyObject *obj)
406408
```
407409
408-
We can call this function from Rust by using [`pyo3::ffi::PyLong_AsUnsignedLongMask`]. This is an *unsafe*
410+
We can call this function from Rust by using [`pyo3::ffi::PyLong_AsUnsignedLongMask`]. This is an _unsafe_
409411
function, which means we have to use an unsafe block to call it and take responsibility for upholding
410412
the contracts of this function. Let's review those contracts:
413+
411414
- The GIL must be held. If it's not, calling this function causes a data race.
412415
- The pointer must be valid, i.e. it must be properly aligned and point to a valid Python object.
413416
414417
Let's create that helper function. The signature has to be `fn(&Bound<'_, PyAny>) -> PyResult<T>`.
418+
415419
- `&Bound<'_, PyAny>` represents a checked borrowed reference, so the pointer derived from it is valid (and not null).
416420
- Whenever we have borrowed references to Python objects in scope, it is guaranteed that the GIL is held. This reference is also where we can get a [`Python`] token to use in our call to [`PyErr::take`].
417421

0 commit comments

Comments
 (0)