Interior Mutability #4418
-
I am working on a GDSII package using Rust and Python here. My problem has come from wanting to allow a user to create a The following preview is how I have been "testing" this, I would like if the print statement is True at the bottom, meaning the library is holding a mutable reference to the Cell object while the cell object is still alive. import gdsr
library = gdsr.Library()
cell = gdsr.Cell("Main")
library.add(cell)
cell.add(gdsr.Polygon([(0, 0), (1, 0), (1, 1), (0, 1)], 0))
new_cell_from_library = next(c for c in library.cells if c.name == "Main")
print(new_cell_from_library is cell) A snippet from my rust code: #[pyclass(eq)]
#[derive(Clone, PartialEq, Default)]
pub struct Cell {
#[pyo3(get, set)]
pub name: String,
#[pyo3(get)]
pub polygons: Vec<Polygon>,
#[pyo3(get)]
pub paths: Vec<Path>,
#[pyo3(get)]
pub references: Vec<Reference>,
#[pyo3(get)]
pub texts: Vec<Text>,
}
#[pyclass]
#[derive(Default)]
pub struct Library {
#[pyo3(get, set)]
pub name: String,
pub cells: Arc<Vec<Arc<Cell>>>,
}
#[pymethods]
impl Library {
#[new]
#[pyo3(signature = (name=String::from("Library")))]
pub fn new(name: String) -> Self {
Library {
name,
cells: Arc::new(Vec::new()),
}
}
#[getter]
pub fn get_cells(&self) -> PyResult<Vec<Cell>> {
Ok(self
.cells
.iter()
.cloned()
.collect::<Vec<Arc<Cell>>>()
.iter()
.map(|cell| *cell.as_ref())
.collect())
}
#[pyo3(signature = (*cells))]
pub fn add(&mut self, cells: Vec<Cell>) -> PyResult<()> {
Arc::get_mut(&mut self.cells)
.unwrap()
.extend(cells.into_iter().map(Arc::new));
Ok(())
}
#[pyo3(signature = (*cells))]
pub fn remove(&mut self, cells: Vec<Cell>) -> PyResult<()> {
let cells = cells.into_iter().map(Arc::new).collect::<Vec<Arc<Cell>>>();
Arc::get_mut(&mut self.cells)
.unwrap()
.retain(|cell| !cells.iter().any(|c| Arc::ptr_eq(cell, c)));
Ok(())
}
} but this does not return the correct Cell objects from get_cells. I would also ideally store cells in a |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 1 reply
-
I believe i have found a solution to this after reading more docs, smh. use std::collections::HashMap;
use pyo3::prelude::*;
use crate::cell::Cell;
pub struct Library {
#[pyo3(get, set)]
pub name: String,
#[pyo3(get)]
pub cells: HashMap<String, Py<Cell>>,
}
#[pymethods]
impl Library {
#[new]
#[pyo3(signature = (name=String::from("Library")))]
pub fn new(name: String) -> Self {
Library {
name,
cells: HashMap::new(),
}
}
#[pyo3(signature = (*cells))]
pub fn add(&mut self, cells: Vec<Py<Cell>>, py: Python) -> PyResult<()> {
for cell in cells {
self.cells
.insert(cell.borrow(py).name.clone(), cell.clone_ref(py));
}
Ok(())
}
#[pyo3(signature = (*cells))]
pub fn remove(&mut self, cells: Vec<Py<Cell>>, py: Python) -> PyResult<()> {
for cell in cells {
self.cells.remove(&cell.borrow(py).name);
}
Ok(())
}
fn __str__(&self) -> PyResult<String> {
let cells_len = self.cells.len();
Ok(format!("Library {} with {} cells", self.name, cells_len))
}
fn __repr__(&self) -> PyResult<String> {
Ok(format!("Library({:?})", self.name))
}
} This now results in the correct output! If anyone has any input on whether this is a good pattern or not i'd greatly appreciate it, otherwise I'm sure I can close this discussion. |
Beta Was this translation helpful? Give feedback.
-
Your solution is correct, in general it is impossible to give out references to inner objects if they aren't wrapped in a |
Beta Was this translation helpful? Give feedback.
Your solution is correct, in general it is impossible to give out references to inner objects if they aren't wrapped in a
Py
pointer. This is because once a reference is in Python land, it can be copied and shared arbitrarily, and the Rust side can't track that.Arc
would be a solution for Rust-only code, andPy
is the equivalent here.