2017-01-12 13 views
2

私はいくつかのデータを返すメソッドで形質を定義しようとしていますが、このデータはimplで変更してはいけません。つまり、一度しか設定できず、 impl。これを確実にする方法はありますか?ここで形質の不変のデータ

は、私は参考のために、C#でこれを実現する方法をです:あなたの質問に

public abstract class Foo 
{ 
    private readonly uint number; 

    public Foo(uint number) { this.number = numbers; } 

    public uint GetNumber() { return number; } 

} 
+5

私は完全な答えがありませんので、私はただコメントを残すつもりです。 1)C#での特性の類似性は、インタフェースに近い。どのようにインターフェイスでこれを解決するアプローチですか? 2)解決しようとしている問題をより高いレベルで説明できますか?なぜその形質の実装が不変のデータを保持する必要があるのですか?なぜそれは不変である必要がありますか? –

+2

XY問題のように見えます。あなたは正確に何をしようとしていますか? – coredump

答えて

2

短い答えはノーです。これをC#のアプローチと同様の方法で実現する方法はありません。幸いにも、RustはC#よりも優れた制御性を提供します。

Rustでの不変性の仕組みと、C#やJavaなどの言語との違いについて理解することが重要です。 C#で

不変

class Foo { 
    readonly Bar bar = new Bar(); 
    uint lives; 
} 

には、いくつかの注意事項:

  • 不変性があたりのフィールドに定義されています。
  • 不変ではありません。たとえば、barへの参照が不変であっても、barが参照する値は変更可能です。
  • C#の不変性は、反射によって簡単に覆されます。 edge casesがあり、そこには反射がなくても転覆することができます。

    struct Foo { 
        bar: Bar, 
        lives: u32 
    } 
    

    最初に注意する錆で

不変性は、構造体の定義は、そのフィールドの不変性の何も言うことです。なぜなら、錆にはフィールドレベルの変異性がないからです。ラストでの可変性が値にを結合上で定義されている:あなたが見ることができるように

// Declare an immutable binding to a Foo 
let foo = Foo { bar: Bar::new(), lives: 10 }; 

// Attempting to mutate the value that foo points to is a compile error 
foo.lives = 5; // compile error! 

foo.bar.baz = 6; // Also a compile error, foo is deeply immutable 

// We can redefine the binding to be mutable 
let foo = mut foo; // foo is now mutable! 

foo.lives = 5; // mutating foo here would be valid 
foo.bar.baz = 6; // this is also valid, foo is deeply mutable 

、錆で可変性は、C#でより簡単でより微妙である:それは変更可能かどうかを決定する値に結合しています、深く変更可能であるか、深く不変であるか*です。

このようなことから、Rustの問題をモデル化してみましょう。 number()selfに不変の結合を要するので、Barを実装する任意のタイプは、呼び出しを介して自分自身を変異することはできません

trait Bar { 
    fn number(&self) -> u32; 
} 

まず、我々は同等GetNumber()方法で形質を定義しますnumber()に:あなたが見ることができるように

struct Foo { 
    number: u32, 
    oranges: u32 
} 

impl Bar for Foo { 
    fn number(&self) -> u32 { 
     self.number += 1; // Compile error. We have an immutable binding to self 
     self.number 
    } 
} 

、ルーストに可変性を制御するすべてのバインディングが定義されている方法を制御についてです。

のはselfに結合する可変を定義し、当社の形質にする方法を紹介しFooに私たちの実装を更新してみましょう:

trait Bar { 
    fn number(&self) -> u32; 
    fn inc_oranges(&mut self); 
} 

impl Bar for Foo { 
    fn number(&self) -> u32 { 
     self.number 
    } 

    fn inc_oranges(&mut self) { 
     // We have a mutable reference to self. We can mutate any part of self: 
     self.oranges += 1; 
     self.number += 1; // We can *also* mutate number 
    } 
} 

あなたがC#のアプローチを好む始めるかもしれない場所です:C#では、あなたはnumberを宣言することができます読み取り専用フィールドの場合はorangesは変更可能ですが、Rustの場合は、修飾可能なバインディングをselfに宣言すると、selfの任意の部分を変更することができます。幸いにも、これを回避する方法があります。

*インテリア可変性

錆はcellモジュールを介して不変結合の一部である方法のmutate値を提供します。長いストーリーを短くするために、これらの型は、変更不可能なバインディングを使用することによって保証されたままで、突然変異を可能にします。これは、そうでなければコンパイル時(ゼロコスト)のチェックをランタイムチェックに移すことによってこれを行います。

のは、今一緒にすべてを入れてみましょう:

impls にselfに不変のバインディングを定義
  • :要するに

    struct Foo { 
        number: u32, 
        orange: Cell(u32) // allow mutation via an immutable binding 
    } 
    
    trait Bar { 
        fn number(&self) -> u32; 
        fn inc_oranges(&self); // self is now an immutable binding 
    } 
    
    impl Bar for Foo { 
        fn number(&self) -> u32 { 
         self.number 
        } 
    
        fn inc_oranges(&self) { 
    
         // We can mutate oranges via cell functions even though self is immutable 
         let cur_oranges = self.oranges.get(); 
         self.oranges.set(cur_oranges + 1); 
    
         self.number += 1; // This would be a compile error 
        } 
    } 
    

    、我々は効果的の方法によって、あなたのC#の例に相当するものを達成することができます

  • セルタイプを使用して、不変のバインディングで内部の変更を許可する

このように、すべてのタイプをモデル化するのは、慣用的ではありません。さらに重要なことは、細胞型を介して特定のフィールドの変異を微調整するのではなく、いつどこで突然変異を許可するのが適切かを知ることです。