内部可变性与数据隐藏以保持固定可变借用的指涉



如果我们运行它,那么我们就会正确收到错误"无法分配给不可变字段a.x"。

如果我们删除两个//注释,并注释掉这个坏行,那么我们会收到错误"无法分配给&引用中的数据"。 这是有道理的,因为&mut不提供内部可变性。 我们可以自由地重新借用&A,所以这不能提供可变的访问权限,唉&&mut&&.

如果我们同时删除//注释和/* */注释,那么整个事情就会编译,允许违反我们不变性的坏行,即a.x永远不能指向其他任何东西。

pub struct A<'a> {
pub x: &'a mut [u8; 3],
}
fn main() {
let y = &mut [7u8; 3];
let /*mut*/ a = A { x: &mut [0u8; 3] };
a.x[0] = 3;
a.x = y;  //// This must be prevented!
{
// let b = &/*mut*/ a;
// b.x[1] = 2;
}
println!("{:?}", a.x);
}

应该如何保持这种x不得改变的不变性? 我们可以在提供公共取消引用方法的同时将字段设为私有,除了为不可接受的A编写构造函数。

我们可以通过将A作为包装器struct AA(A)的私有成员来避免令人讨厌的构造函数,包装器本身承载公共取消引用方法。 现在AA需要一个简单的构造函数,但它不需要所有A字段的参数,不会影响执行顺序等。 但是,如果我们需要为AAA实现一些特征,这将变得痛苦。

然而,另一种方法是通过处理Cell<A>来使用内部可变性,使用Cell::replace访问它,并在以后将其放回。 这听起来很有问题,但表明存在更多的解决方案。

有什么更清洁的方法吗?

与其使用Cell<A>,不如让A内的数组包含Cell<u8>s:

use std::cell::Cell;
pub struct A<'a> {
x: &'a [Cell<u8>; 3],
}
fn main() {
// let y = &mut [7u8; 3];
let a = A { x: &[Cell::new(0u8), Cell::new(0u8), Cell::new(0u8)] };
a.x[0].set(3);
// a.x = y;
{
let b = &a;
b.x[1].set(2);
}
println!("{:?}", a.x);
}

这仍然会按照您想要的方式运行,具有相同的性能,但现在a变量是不可变的,因此您无法更改a.x.您也不需要使数组引用可变。

您的示例的轻微缺点是不能使用数组重复语法,因为Cell<T>没有实现Copy。这似乎是一个遗漏,但有一些解释为什么在这里。

另一种方法是定义

pub struct A<'a> { pub x: HideMut<'a,[u8; 3]> }

哪里

use std::ops::{Deref,DerefMut};
struct HideMut<'a,T>(&'a mut T) where T: ?Sized + 'a;
impl<'a,T> HideMut<'a,T> where T: ?Sized {
pub fn new(m: &'a mut T) -> HideMut<'a,T> { HideMut(m) }
}
impl<'a,T> Deref for HideMut<'a,T> where T: ?Sized {
type Target = T;
fn deref(&self) -> &T { self.0 }
}
impl<'a,T> DerefMut for HideMut<'a,T> where T: ?Sized {
fn deref_mut(&mut self) -> &mut T { self.0 }
}

如前所述,这并不能阻止问题本身,但它需要您调用HideMut::new()构造函数来违反它。

现在,如果我们在与A相同的模块中定义HideMut,甚至可能不导出它,那么我们实际上确实实现了所需的隐藏,而没有任何内部可变性。

第二种形式不符合我最初的要求,因为现在你不能使用构造函数A { },但根据你不想为A编写构造函数的原因,它可能会奏效。

无论哪种形式,这都避免了像一种方法那样借用整个A

最新更新