2017-08-09 89 views
0

我需要匹配來自方法的self上的可選值,並基於該匹配,調用self上的方法,該方法可變地取self。我試圖做一個相當高性能的遊戲,所以儘量避免可變性,我不能在這種情況下:方法確實需要可變地訪問結構,他們確實需要根據結構屬性進行調度。這是一個MVCE:訪問Rust中訪問屬性後調用方法

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. CopyClonebar對象,並解開如何,只要你想。
  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

我想我會選擇第三種方式!我試着克隆這個選項,但由於某種原因它沒有幫助,我也試着像你說的那樣匹配這個選項,但它並不起作用,但是根據我所瞭解的借用跳棋規則,我認爲最後一個會起作用。謝謝! –

+0

@ChristopherDumas我編輯了代碼,以便它不再使用「Box」,而是使用原始函數指針。 –

2
  1. 可以解開的比賽,而不是使用if letOption(Baz) ...
  2. 申報富作爲可變

這裏是代碼...

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