For example given this code:
use std::rc::Rc;
use std::cell::RefCell;
// Don't want to copy for performance reasons
struct LibraryData {
// Fields ...
}
// Creates and mutates data field in methods
struct LibraryStruct {
// Only LibraryStruct should have mutable access to this
data: Rc<RefCell<LibraryData>>
}
impl LibraryStruct {
pub fn data(&self) -> Rc<RefCell<LibraryData>> {
self.data.clone()
}
}
// Receives data field from LibraryStruct.data()
struct A {
data: Rc<RefCell<LibraryData>>
}
impl A {
pub fn do_something(&self) {
// Do something with self.data immutably
// I want to prevent this because it can break LibraryStruct
// Only LibraryStruct should have mutable access
let data = self.data.borrow_mut();
// Manipulate data
}
}
How can I prevent LibraryData
from being mutated outside of LibraryStruct
? LibraryStruct
should be the only one able to mutate data
in its methods. Is this possible with Rc<RefCell<LibraryData>>
or is there an alternative? Note I'm writing the "library" code so I can change it.
If you share a RefCell
then it will always be possible to mutate it - that's essentially the whole point of it. Given that you are able to change the implementation of LibraryStruct
, you can make sure that data
is not public, and control how it is exposed to its users through a getter method:
pub struct LibraryStruct {
// note: not pub
data: Rc<RefCell<LibraryData>>
}
impl LibraryStruct {
// could also have returned `Ref<'a, LibraryData> but this hides your
// implementation better
pub fn data<'a>(&'a self) -> impl Deref<Target = LibraryData> + 'a {
self.data.borrow()
}
}
In your other struct, you can keep things simple, by just treating it as a reference:
pub struct A<'a> {
data: &'a LibraryData,
}
impl<'a> A<'a> {
pub fn do_something(&self) {
// self.data is only available immutably here because it's just a reference
}
}
fn main() {
let ld = LibraryData {};
let ls = LibraryStruct { data: Rc::new(RefCell::new(ld)) };
let a = A { data: &ls.data() };
}
If you need to hold the reference for longer, during which time the original RefCell
needs to be mutably borrowed in the library code, then you need to make a custom wrapper which can manage that. It's possible that there's a standard library type for this, but I don't know of it and it's easy to make something specifically for your use case:
// Wrapper to manage a RC<RefCell> and make it immutably borrowable
pub struct ReadOnly<T> {
// not public
inner: Rc<RefCell<T>>,
}
impl<T> ReadOnly<T> {
pub fn borrow<'a>(&'a self) -> impl Deref<Target = T> + 'a {
self.inner.borrow()
}
}
Now return this in your library code:
impl LibraryStruct {
pub fn data<'a>(&'a self) -> ReadOnly<LibraryData> {
ReadOnly { inner: self.data.clone() }
}
}
And when you use it, the inner RefCell
will not be directly accessible and the data is only available to borrow immutably:
pub struct A {
data: ReadOnly<LibraryData>,
}
impl A {
pub fn do_something(&self) {
// data is immutable here
let data = self.data.borrow();
}
}
fn main() {
let ld = LibraryData {};
let ls = LibraryStruct { data: Rc::new(RefCell::new(ld)) };
let a = A { data: ls.data() };
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With