是否有替代方法或方法让 Rc<RefCell<X>> 限制 X 的可变性?
Is there an alternative or way to have Rc<RefCell<X>> that restricts mutability of X?
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
}
}
如何防止 LibraryData
在 LibraryStruct
之外发生变异? LibraryStruct
应该是唯一能够在其方法中改变 data
的方法。这可能与 Rc<RefCell<LibraryData>>
还是有替代方案?请注意,我正在编写 "library" 代码以便我可以更改它。
如果你分享一个 RefCell
那么它总是可以改变它 - 这基本上就是它的全部意义。鉴于您能够更改 LibraryStruct
的实现,您可以确保 data
不是 public,并通过 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()
}
}
现在 return 在您的库代码中:
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() };
}
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
}
}
如何防止 LibraryData
在 LibraryStruct
之外发生变异? LibraryStruct
应该是唯一能够在其方法中改变 data
的方法。这可能与 Rc<RefCell<LibraryData>>
还是有替代方案?请注意,我正在编写 "library" 代码以便我可以更改它。
如果你分享一个 RefCell
那么它总是可以改变它 - 这基本上就是它的全部意义。鉴于您能够更改 LibraryStruct
的实现,您可以确保 data
不是 public,并通过 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()
}
}
现在 return 在您的库代码中:
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() };
}