2017-08-09 11 views
0

selfにある任意の値と一致させる必要があり、一致する場合はselfのメソッドを呼び出して、selfを変更します。私はかなり高性能のゲームをやろうとしていますので、私はこのような状況では不可能です。つまり、メソッドは実際に構造体にアクセスする必要があります。構造体のプロパティに基づいてディスパッチされます。ここにMVCEがあります:Rustでプロパティにアクセスした後に&mut selfを呼び出すメソッド

enum Baz { 
    A, 
    B, 
    C, 
} 
struct Foo { 
    bar: Option<Baz>, 
} 

impl Foo { 
    pub fn dostuff(&mut self, fizz: i32) { 
     if let Some(ref b) = self.bar { 
      match b { 
       &Baz::A => self.do_a(fizz), 
       &Baz::B => self.do_b(fizz + 2), 
       &Baz::C => self.do_c(fizz + 1), 
      } 
     } 
    } 

    pub fn do_a(&mut self, f: i32) { 
     println!("A, with fizz {}", f); 
    } 
    pub fn do_b(&mut self, f: i32) { 
     println!("B, with fizz {}", f); 
    } 
    pub fn do_c(&mut self, f: i32) { 
     println!("C, with fizz {}", f); 
    } 
} 

fn main() { 
    let foo = Foo { bar: Some(Baz::A) }; 
    foo.dostuff(3); 
} 

ここにはthe playgroundです。

error[E0502]: cannot borrow `*self` as mutable because `self.bar.0` is also borrowed as immutable 
    --> src/main.rs:14:28 
    | 
12 |   if let Some(ref b) = self.bar { 
    |      ----- immutable borrow occurs here 
13 |    match b { 
14 |     &Baz::A => self.do_a(fizz), 
    |       ^^^^ mutable borrow occurs here 
... 
18 |   } 
    |   - immutable borrow ends here 

私はボローチェッカーで自分の平和を作ったが、どうやらないと思った。なぜこれが起こっている私が知っていますが、私は、この問題を解決する方法は考えています。誰かが将来これを避ける方法を説明したら、私はそれを感謝します。

+1

"アクセスした後に"アクセスしただけでなく、その参照を保持した後 "プロパティにアクセスした後"。 'do_a' /' do_b'/'do_c'が' self.b'を変更しようとすると、不変な参照が変更されたため、Rustのメモリ安全ルールに違反します。 – Shepmaster

答えて

1

あなたが気にした場合私が知る限り、Rustの規則はあなたにそのようなコードを持つことを禁じています。

  1. CopyまたはCloneあなたbar対象としたいとどのようアンラップ:あなたは、のいずれかを持っています。
  2. フラグを追加し、オブジェクトに複数の参照がないようにします。たとえば、何をするかを指示する2つの変数を追加します。列挙型と一致し、これらの変数を設定し、次にこれらの変数に一致させます。これはここの最初の項目よりもあまり役に立ちませんが、これも解決策です。

  3. は様よりに機能する:

    enum Baz { 
        A, 
        B, 
        C, 
    } 
    struct Foo { 
        bar: Option<Baz>, 
    } 
    
    impl Foo { 
        pub fn dostuff(&mut self, fizz: i32) { 
         let (method, arg) = match self.bar { 
          Some(ref b) => { 
           match b { 
            &Baz::A => (Self::do_a as fn(&mut Self, i32)>, fizz), 
            &Baz::B => (Self::do_b as fn(&mut Self, i32)>, fizz + 2), 
            &Baz::C => (Self::do_c as fn(&mut Self, i32)>, fizz + 1), 
           } 
          }, 
          None => return, 
         }; 
         method(self, arg); 
        } 
    
        pub fn do_a(&mut self, f: i32) { 
         println!("A, with fizz {}", f); 
        } 
        pub fn do_b(&mut self, f: i32) { 
         println!("B, with fizz {}", f); 
        } 
        pub fn do_c(&mut self, f: i32) { 
         println!("C, with fizz {}", f); 
        } 
    } 
    
    fn main() { 
        let mut foo = Foo { bar: Some(Baz::A) }; 
        foo.dostuff(3); 
    } 
    

    そうすることによって、あなたはちょうどあなたがする必要があるものを呼び出すので、それは、引数のをあなたたい呼び出し方法を返します。私はかなりこのソリューションはBoxを使用せずに書き直すことができると確信していますが、メソッドへの参照だけが、私は方法を知らない。

+1

私は第3の選択肢に行くと思います!私はオプションをクローンアップしようとしましたが何らかの理由でそれが役に立たなかったし、あなたが言ったようにオプションをマッチさせてみましたが、うまくいきませんでしたが、 。ありがとう! –

+0

@ChristopherDumas私はコードを編集して、もう 'Box'を使わずに生の関数ポインタを使うようにしました。 –

2
  1. あなたが代わりにif letを使用しての試合中Option(Baz)のラップを解除することができます。..ここ

がコードであるとして、可変

  • 宣言fooの...

    enum Baz { 
        A, 
        B, 
        C, 
    } 
    struct Foo { 
        bar: Option<Baz>, 
    } 
    
    impl Foo { 
        pub fn dostuff(&mut self, fizz: i32) { 
         match self.bar { 
          Some(Baz::A) => self.do_a(fizz), 
          Some(Baz::B) => self.do_b(fizz + 2), 
          Some(Baz::C) => self.do_c(fizz + 1), 
          None => {}, 
         } 
        } 
    
        pub fn do_a(&mut self, f: i32) { 
         println!("A, with fizz {}", f); 
        } 
        pub fn do_b(&mut self, f: i32) { 
         println!("B, with fizz {}", f); 
        } 
        pub fn do_c(&mut self, f: i32) { 
         println!("C, with fizz {}", f); 
        } 
    } 
    
    fn main() { 
        let mut foo = Foo { bar: Some(Baz::B) }; 
        foo.dostuff(3); 
    } 
    
  • 関連する問題