85184


最近Rustを始めた初心者ですが、タイトルのような状況でどうすればよいかと考えています。

ある処理を後でまとめて行うためにフィールドの`vec`に処理を`enum`として格納し、まとめて実行するために以下のようなコードを書きました。

[code]

    struct Data {
        data: Vec<u8>,
        operations: Vec<Operation>,
    }
    
    enum Operation {
        A(usize),
        B(usize),
        C(usize, usize)
    }
    
    impl Data {
        fn run(&mut self) {
            for op in self.operations.iter() {
                match *op {
                    Operation::A(x) => self.a(x),  // ここでエラー
                    Operation::B(x) => self.b(x),  // ここでエラー
                    Operation::C(x, y) => self.c(x, y),  // ここでエラー
                }
            }
        }
    
        fn a(&mut self, x: usize) {
            // self.dataに対する何らかの操作
            unimplemented!()
        }
        fn b(&mut self, x: usize) {
            // self.dataに対する何らかの操作
            unimplemented!()
        }
        fn c(&mut self, x: usize, y: usize) {
            // self.dataに対する何らかの操作
            unimplemented!()
        }
    
       // operationを追加するメソッドは省略
    }
    
[/code]

しかし、mutableとimmutableの参照が同時には存在できないというエラーが出てしまいます。

>
[code]

>     error[E0502]: cannot borrow `*self` as mutable because it is also
> borrowed as immutable
>      --> src/main.rs:20:36
>       |
>     18 |         for op in self.operations.iter() {
>       |                   ----------------------
>       |                   |
>       |                   immutable borrow occurs here
>       |                   immutable borrow later used here
>     19 |             match *op {
>     20 |                 Operation::A(x) => self.a(x),
>       |                                    ^^^^^^^^^ mutable borrow occurs
> here
>  
[/code]

また、評価のために`run`メソッドを何度も呼び出したいと考えているため、`run`メソッド内で`operations`をコピーしてしまうのはオーバーヘッドの問題から避けたいと考えています。

極力オーバーヘッドが小さい方法でこのエラーに対処するなにか良い方法があればご教示いただきたいです。


CC BY-SA 4.0
2021-12-16T18:45:49.693
85176
2021-12-17T03:32:54.077
50535
question
rust
構造体のフィールドのvecをループさせながら、内容に応じてミュータブルなメソッドを呼び出すときはどのようにすればよいですか？


`self`絡みの所有権エラーは構造体を分割すると楽になることがよくあります（[Playground](https://play.rust-
lang.org/?version=stable&mode=debug&edition=2021&gist=a44295b1781f76e896ccd918b123129a)）。この例では
`data` と `operation` の所有者を分けています。

[code]

    enum Operation {
        A(usize),
        B(usize),
        C(usize, usize)
    }
    
    struct Dispatcher {
        operations: Vec<Operation>,
    }
    
    impl Dispatcher {
        fn run(&mut self, worker: &mut Worker) {
            for op in self.operations.iter() {
                match *op {
                    Operation::A(x) => worker.a(x), 
                    Operation::B(x) => worker.b(x), 
                    Operation::C(x, y) => worker.c(x, y),
                }
            }
        }
        
        fn add(&mut self, op: Operation) {
            self.operations.push(op); 
        }
    }
    
    struct Worker {
        data: Vec<u8>
    }
    
    impl Worker {
        fn a(&mut self, x: usize) {
            println!("Executing A({}): data[0] = {}", x, self.data[0]);
        }
        fn b(&mut self, x: usize) {
            println!("Executing B({})", x);
        }
        fn c(&mut self, x: usize, y: usize) {
            println!("Executing C({}, {})", x, y);
        }
    }
    
    fn main() {
        let mut worker = Worker { data: vec![1, 2, 3] };
        let mut dispatcher = Dispatcher { operations: vec![Operation::A(3), Operation::B(4)] };
        dispatcher.add(Operation::C(5, 6));
        
        dispatcher.run(&mut worker);
    }
    
[/code]

なお、`usize` が `Copy` を実装しているため、それで構成される `Operation` も `Copy`
を実装しています。したがって、`match *op`
で参照を外すときに`operations`の要素がそれぞれコピーされています（一個ずつではありますが）。


CC BY-SA 4.0
2021-12-17T02:47:02.610
85183
2021-12-17T02:47:02.610
7500
85176
answer


`a`,`b`,`c`が`data`のみを変更するのであれば,`Data`全体を受け取る代わりに`[u8]`を受け取るように書き換えるのがよいと思います.

[code]

    // DataとOperationの定義はそのまま
    
    impl Data {
        fn run(&mut self) {
            // dataとoperationsの所有権を個別に扱う
            let Self { data, operations } = self;
            for op in operations.iter() {
                match *op {
                    Operation::A(x) => Self::a(data, x),
                    Operation::B(x) => Self::b(data, x),
                    Operation::C(x, y) => Self::c(data, x, y),
                }
            }
        }
    
        fn a(data: &mut [u8], x: usize) {
            data[x] += 1;
        }
        fn b(data: &mut [u8], x: usize) {
            data[x] += 2;
        }
        fn c(data: &mut [u8], x: usize, y: usize) {
            data[x] += 3;
            data[y] += 4;
        }
    }
    
    fn main() {
        let mut data = Data {
            data: vec![0; 6],
            operations: vec![
                Operation::A(1),
                Operation::B(2),
                Operation::C(3, 4),
                Operation::A(5),
            ],
        };
    
        data.run();
        println!("{:?}", data.data);
        data.run();
        println!("{:?}", data.data);
    }
    
[/code]

**実行結果**

[code]

    [0, 1, 2, 3, 4, 1]
    [0, 2, 4, 6, 8, 2]
    
[/code]


CC BY-SA 4.0
2021-12-17T03:32:54.077
85184
2021-12-17T03:32:54.077
13199
85176
answer