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