Если вы поделитесь RefCell
, то всегда будет возможно изменить его - это, по сути, весь смысл. Учитывая, что вы можете изменить реализацию LibraryStruct
, вы можете убедиться, что data
не является общедоступным, и контролировать то, как он предоставляется пользователям, с помощью метода getter:
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()
}
}
В другой структуре вы можете упростить задачу, просто обратившись к ней как к справочнику:
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() };
}
Если вам нужно дольше хранить ссылку, в течение которой оригинал RefCell
должен быть заимствован в коде библиотеки, то вам нужно создать специальную оболочку, которая сможет этим управлять. Возможно, для этого есть стандартный тип библиотеки, но я об этом не знаю, и легко сделать что-то специально для вашего случая использования:
// 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()
}
}
Теперь верните это в код вашей библиотеки:
impl LibraryStruct {
pub fn data<'a>(&'a self) -> ReadOnly<LibraryData> {
ReadOnly { inner: self.data.clone() }
}
}
И когда вы используете его, внутренний RefCell
не будет напрямую доступен, а данные доступны только для неизменного заимствования:
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() };
}