PyO3: How to return a mutable reference of rust object to Python

I have a struct:

#[pyclass]
pub struct Polygon {
    pub vertices: Vec<Point>,
}

for which I want to implement a mutable access to an arbitrary vertex:

#[pymethods]
impl Polygon {
    pub fn vertex<'a>(&'a mut self, i:size) -> PyResult<&'a mut Point> {}
}

How can I achieve this? I'm flexible about the Polygon::vertex() method signature; I just want this to work in Python:

poly = Polygon()
poly.vertex(1).x = 7
print(poly.vertex(1)) # should show that x is 7

All of the examples I've came across return a clone (deep copy) of a field (a vertex in my case)

I haven't personally used PyO3 for anything, but I'm pretty sure you'll find this is impossible. The problem is that there's no way to get Python to automatically obey the rules of exclusive references; this is valid Python code, but a borrow conflict in the underlying Rust if it worked the way you want:

poly = Polygon()
vert = poly.vertex(1)  # takes mutable ref 1?
print(poly.vertex(1))  # takes mutable ref 2?
vert.x = 7             # uses mutable ref 1 which must be invalid?
print(poly.vertex(1))

So, in order for this to succeed, or fail soundly, you need either to not take &mut (and use an Arc<Mutex<... or similar for the mutable data for a single vertex) or to somehow cause all other operations to fail while the mutable borrow exists, which requires some kind of run-time checking for the Python-called functions to do (which, if done correctly, would be quite similar to a Mutex anyway).

1 Like

Objects in Python are mutable by default unless flagged as frozen with #[pyclass(frozen)]. I think what you're after is to simply implement &mut self setter methods on Point, then any owned Point which you access should be settable.

However, that still leaves you with the issue that indexing in Rust gives a reference and PyO3 classes cannot hold lifetimes (guide). You could look at how subclassing of collections is handled (guide, again), but the easier answer might be to just hold a PyList instead of a Vec<T> and let Python handle the reference counting when accessing the list.