質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

Q&A

2回答

594閲覧

Rustでselfを可変として扱う関数内から不可変として扱う関数を呼び出したい。あるいは他の解決策を知りたい。

skuralll

総合スコア0

Rust

Rustは、MoFoが支援するプログラミング言語。高速性を維持しつつも、メモリ管理を安全に行うことが可能な言語です。同じコンパイル言語であるC言語やC++では困難だったマルチスレッドを実装しやすく、並行性という点においても優れています。

0グッド

1クリップ

投稿2023/01/11 16:01

前提

Rustでライフゲームを作成しようとしています。
盤面を管理する構造体Boardを作成し、メンバ関数を実装したところエラーが発生しました。
どうやらstep関数内でselfが可変のものとして使用されているのにもかかわらず、selfを不可変なものとして扱うget_cell関数を関数内で呼び出している為エラーが出力されるようです。
エラーの原因はわかったのですが、上手い解決法が見つからない為、ご教授願いたいです。

実現したいこと

メンバ関数stepからget_cell関数を呼び出せるようにしたい。
あるいは他の解決策を知りたい。

発生している問題・エラーメッセージ

error[E0502]: cannot borrow `*self` as immutable because it is also borrowed as mutable --> src/game.rs:65:44 | 59 | for cell in self.cells.iter_mut(){ | --------------------- | | | mutable borrow occurs here | mutable borrow later used here ... 65 | if let Some(around_cell) = self.get_cell(x, y){ | ^^^^^^^^^^^^^^^^^^^ immutable borrow occurs here

該当のソースコード

rust//セル

1pub struct cell { 2 // 座標 3 x: i32, 4 y: i32, 5 // 生死 6 value: bool, 7} 8 9//盤面 10pub struct Board{ 11 // サイズ 12 width: i32, 13 height: i32, 14 // セルを格納するベクタ 15 cells: Vec<cell>, 16} 17 18const AROUND_POS: [(i32, i32); 8] = [ 19 (-1, -1), 20 (-1, 0), 21 (-1, 1), 22 (0, -1), 23 (0, 1), 24 (1, -1), 25 (1, 0), 26 (1, 1), 27]; 28 29impl Board { 30 // 盤面を新規生成する 31 pub fn new(width: i32, height: i32) -> Board { 32 let mut cells = Vec::new(); 33 for y in 0..height { 34 for x in 0..width { 35 cells.push(cell{x: x, y: y, value: false}); 36 } 37 } 38 Self {cells: cells, width: width, height: height} 39 } 40 41 //指定の座標のセルを取得する 42 pub fn get_cell(&self, x: i32, y: i32) -> Option<&cell> { 43 //両端を繋げないための処理 44 if 0 > x && x >= self.width { 45 return None; 46 } 47 //セルが存在していれば返す 48 if let Some(cell) = self.cells.get( (x + y*self.width) as usize) { 49 Some(cell) 50 } 51 else{ 52 None 53 } 54 } 55 56 // 1ステップ進める 57 pub fn step(&mut self){ 58 for cell in self.cells.iter_mut(){ 59 //周りの生存セル数をカウントする 60 let mut around_count = 0; 61 for pos in AROUND_POS.iter(){ 62 let x = cell.x + pos.0; 63 let y = cell.y + pos.1; 64 if let Some(around_cell) = self.get_cell(x, y){ 65 if around_cell.value{ 66 around_count += 1; 67 } 68 } 69 } 70 // 生死判定 71 if cell.value{ 72 // 生存セル 73 if around_count < 2 || around_count > 3{ 74 // 過疎or過密 75 cell.value = false; 76 } 77 } 78 else{ 79 // 死亡セル 80 if around_count == 3{ 81 // 誕生 82 cell.value = true; 83 } 84 } 85 } 86 } 87}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

原則としてはそのような状況にならないように設計するに越したことはないのですが、どうしてもプログラムの構造はいじれないというときには std::cell::Cellstd::cell::RefCell などを活用できるかもしれません。

これらを使えば不変参照を経由しているときであっても内容の書換が出来るようになったり、本来は出来ないはずの参照を得られたりします。 コンパイラのチェックを抜けられますが実行時に危険なアクセスをしていないかはチェックされる (駄目ならパニックする) ようになってます。

rust

1use std::cell::RefCell; 2 3pub struct Cell { 4 // 座標 5 x: i32, 6 y: i32, 7 // 生死 8 value: RefCell<bool>, 9} 10 11//盤面 12pub struct Board { 13 // サイズ 14 width: i32, 15 height: i32, 16 // セルを格納するベクタ 17 cells: Vec<Cell>, 18} 19 20const AROUND_POS: [(i32, i32); 8] = [ 21 (-1, -1), 22 (-1, 0), 23 (-1, 1), 24 (0, -1), 25 (0, 1), 26 (1, -1), 27 (1, 0), 28 (1, 1), 29]; 30 31impl Board { 32 // 盤面を新規生成する 33 pub fn new(width: i32, height: i32) -> Board { 34 let mut cells = Vec::new(); 35 for y in 0..height { 36 for x in 0..width { 37 cells.push(Cell { 38 x: x, 39 y: y, 40 value: std::cell::RefCell::new(false), 41 }); 42 } 43 } 44 Self { 45 cells: cells, 46 width: width, 47 height: height, 48 } 49 } 50 51 //指定の座標のセルを取得する 52 pub fn get_cell(&self, x: i32, y: i32) -> Option<&Cell> { 53 //両端を繋げないための処理 54 if 0 > x && x >= self.width { 55 return None; 56 } 57 //セルが存在していれば返す 58 if let Some(cell) = self.cells.get((x + y * self.width) as usize) { 59 Some(cell) 60 } else { 61 None 62 } 63 } 64 65 // 1ステップ進める 66 pub fn step(&mut self) { 67 for cell in self.cells.iter() { 68 //周りの生存セル数をカウントする 69 let mut around_count = 0; 70 for pos in AROUND_POS.iter() { 71 let x = cell.x + pos.0; 72 let y = cell.y + pos.1; 73 if let Some(around_cell) = self.get_cell(x, y) { 74 if *around_cell.value.borrow() { 75 around_count += 1; 76 } 77 } 78 } 79 // 生死判定 80 if *cell.value.borrow() { 81 // 生存セル 82 if around_count < 2 || around_count > 3 { 83 // 過疎or過密 84 *cell.value.borrow_mut() = false; 85 } 86 } else { 87 // 死亡セル 88 if around_count == 3 { 89 // 誕生 90 *cell.value.borrow_mut() = true; 91 } 92 } 93 } 94 } 95}

投稿2024/08/18 11:15

SaitoAtsushi

総合スコア5714

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

私も初心者なのでちょっと怪しいですが、Rustでは可変参照一つか不変参照複数のどちらかしか存在できないのでエラーが生じていると考えられます。
そのためiter_mutのループ内で&selfを呼び出すことは難しいかと思います。
同様の動作をするコードとして次のようにすればコンパイルは通るかと思います。

Rust

1pub fn step(&mut self){ 2 let next_state = self.cells 3 .iter() 4 .map(|cell| { 5 //周りの生存セル数をカウントする 6 let mut around_count = 0; 7 8 for pos in AROUND_POS.iter(){ 9 let x = cell.x + pos.0; 10 let y = cell.y + pos.1; 11 if let Some(around_cell) = self.get_cell(x, y){ 12 if around_cell.value{ 13 around_count += 1; 14 } 15 } 16 } 17 let mut next_cell_value = cell.value; 18 // 生死判定 19 if next_cell_value{ 20 // 生存セル 21 if around_count < 2 || around_count > 3{ 22 // 過疎or過密 23 let next_cell_value = false; 24 } 25 } 26 else{ 27 // 死亡セル 28 if around_count == 3{ 29 // 誕生 30 next_cell_value = true; 31 } 32 } 33 next_cell_value 34 }).collect::<Vec<bool>>(); 35 36 next_state.into_iter().zip(self.cells.iter_mut()) 37 .for_each(|(next_value, cell)| cell.value = next_value); 38}

投稿2024/08/18 00:09

super_omega_max

総合スコア18

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問