2

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

如果我们删除这两个//注释,并注释掉这个坏行,那么我们会得到错误“无法分配给&引用中的数据”。这是有道理的,因为&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不可更改的不变量?我们可以在提供公共解引用方法的同时将字段设为私有,除了A不可接受的为 in 编写构造函数。

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

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

任何更清洁的方法?

4

2 回答 2

1

Cell<A>您可以在 contains 中创建数组,A而不是使用 a Cell<u8>

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. 这似乎是一个遗漏,但有一些解释为什么会出现在这里

于 2017-02-21T05:04:24.270 回答
1

另一种方法是定义

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()构造函数来违反它。

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

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

A在任何一种形式中,这都避免了像方法那样借用整个 of 。

于 2017-02-21T19:23:42.027 回答