2017-02-21 6 views
2

thisを実行すると、「不可能なフィールドa.xに割り当てられません」というエラーが正しく表示されます。内部の可変性とデータの隠蔽性の違いがあり、変更可能な借用の参照を固定している場合

2つのコメントを削除し、この悪い行をコメントアウトすると、「&の参照にデータを割り当てることができません」というエラーが表示されます。 &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のコンストラクタを書くことが容認できないことを除いて、パブリックの参照解除メソッドを提供しながらフィールドをプライベートにすることができます。

我々はA自体がパブリックデリファレンス・メソッドをホストするラッパーstruct AA(A)のプライベートメンバにすることによって不快なコンストラクタを回避することができます。今度はAAは簡単なコンストラクタを必要としますが、Aのすべてのフィールドに引数を必要とせず、実行順序などに影響を与えません。AAAの両方に実装されたいくつかの特性が必要な場合、

Cell<A>で作業し、Cell::replaceでアクセスして後で戻すことで、内部の変更を使用する方法もあります。これは非常に問題が多いようですが、より多くの解決策が存在することを示しています。

どんなクリーナーアプローチですか?

+0

もう1つの方法は、配列の中で 'Cell'を使うことです。それを試しましたか? –

答えて

1

よりもむしろあなたがA内部の配列がCell<u8> S含有させることができCell<A>使用しますので、これはまだ同じパフォーマンスで、あなたが望むどのように動作しますが、今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.xを変更することはできません。また、配列参照を変更可能にする必要もありません。

例では、Cell<T>にはCopyが実装されていないため、配列の繰り返し構文を使用できないという欠点があります。これは省略のようですが、なぜそれがhereであるかという説明があります。

+0

興味深い。 :)私は 'x:Cell <[u8; 3]>'でも配列の構文をとることができると思いますが、それはより多くのデータを動かす危険があります。実際には、私はこれらの 'x'の束に別の場所からスライスを切り詰めているので、' Cell'を使うために 'transmute'が必要ですが、これは知ってうれしいです。 'Cell <&mut [T]>'などの 'split_at_mut'があれば、おそらく' x:Cell <[u8; 3]> 'バージョンを使うことができます。ありがとう! –

+0

あなたは '' A = A {x:&unsafe {mem :: transmute([0u8; 3])}}; 'と言っていますが、それはまったくかわいいものではありません:) –

+0

私は、 '&mut [T]'と '&mut [Cell ]の間、'&mut [T; n] 'と'&mut [Cell ; n] 'は役に立つと思う。 :) –

1

別のアプローチが書かれたよう

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 } 
} 

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

を定義することで、これは、それ自体が問題を防ぐことはできませんが、それはあなたがそれに違反するHideMut::new()コンストラクタを呼び出す必要です。

Aと同じモジュールにHideMutを定義し、それをエクスポートしていない場合でも、実際に内部の変更なしで希望の隠蔽を実現します。

コンストラクタA { }を使用できないため、この第2の形式は当初の要件を満たしていませんが、Aのコンストラクタを記述したくない理由によってうまくいく可能性があります。

どちらの形式でも、これは方法としてAの全体を借りることを避けます。

関連する問題