内部可变性与数据隐藏以固定可变借用的引用
Interior mutability vs data hiding to hold fixed the referant of a mutable borrow
如果我们 运行 this 那么我们正确地得到错误 "cannot assign to immutable field a.x
"。
如果我们删除两个 //
注释,并注释掉这一行错误,那么我们会得到错误 "cannot assign to data in a &
reference"。这是有道理的,因为 &mut
不提供内部可变性。我们可以自由地重新借用一个 &A
,所以这不能提供可变访问,ala &&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
不能更改的不变量?我们可以在提供 public 解引用方法的同时将字段设为私有,除了为 A
编写构造函数是不可接受的。
我们可以通过使 A
成为包装器 struct AA(A)
的私有成员来避免讨厌的构造函数,包装器 struct AA(A)
本身承载 public 取消引用方法。现在 AA
需要一个简单的构造函数,但它不需要 A
的所有字段的参数,不影响执行顺序等。如果我们需要为两个 [=22] 实现一些特征,这会变得很痛苦=] 和 AA
虽然。
然而,另一种方法是通过使用 Cell<A>
来使用内部可变性,使用 Cell::replace
访问它,然后再放回去。这听起来很有问题,但表明存在更多解决方案。
有更清洁的方法吗?
您可以使 A
中的数组包含 Cell<u8>
s:
而不是使用 Cell<A>
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
。这似乎是一个遗漏,但对于为什么是 here.
有一些解释
另一种方法是定义
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
。
如果我们 运行 this 那么我们正确地得到错误 "cannot assign to immutable field a.x
"。
如果我们删除两个 //
注释,并注释掉这一行错误,那么我们会得到错误 "cannot assign to data in a &
reference"。这是有道理的,因为 &mut
不提供内部可变性。我们可以自由地重新借用一个 &A
,所以这不能提供可变访问,ala &&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
不能更改的不变量?我们可以在提供 public 解引用方法的同时将字段设为私有,除了为 A
编写构造函数是不可接受的。
我们可以通过使 A
成为包装器 struct AA(A)
的私有成员来避免讨厌的构造函数,包装器 struct AA(A)
本身承载 public 取消引用方法。现在 AA
需要一个简单的构造函数,但它不需要 A
的所有字段的参数,不影响执行顺序等。如果我们需要为两个 [=22] 实现一些特征,这会变得很痛苦=] 和 AA
虽然。
然而,另一种方法是通过使用 Cell<A>
来使用内部可变性,使用 Cell::replace
访问它,然后再放回去。这听起来很有问题,但表明存在更多解决方案。
有更清洁的方法吗?
您可以使 A
中的数组包含 Cell<u8>
s:
Cell<A>
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
。这似乎是一个遗漏,但对于为什么是 here.
另一种方法是定义
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
。