MENU閉じる

HEXA BLOG

プログラム

HEXA BLOGプログラム2023.5.22

rustが難しい話-入門編

sudoやsuがrustで書き直されるプロジェクトが予定されていると聞いて
ついにrustの時代到来か⁉とワクワクが止まらないgood sun🌞こと山口です。
windowsのinsider版にもrust製のプログラムが含まれ始めるようですね。

今回はそんなrustを従来の思考で使用すると難しいという話です。

今回はゲームループの中での要素ごとの振る舞いをポリモーフィズムを使って書く簡単なサンプルを書いて見ました。
ソースは全文は以下の通りです。

流れとしては2種類のユニットを更新して寿命が尽きたら後継のユニットを生成してそれを管理するというのを永遠と繰り返すだけのプログラムです。

今回はC++的な感覚でtraitを使って実装してみます。
rustにはclassや継承が無くinterface的な機能を持つのがtraitです。

こんな感じで更新処理と死亡判定を持つようなtraitを定義します

trait Task {
fn update(&mut self);
fn is_died(&self)->bool;
fn spawn(&self)->Box<dyn Task>;
}

そしてstructにtraitを実装します

struct UnitA {
name : String,
life : i32,
}
impl Task for UnitA{
fn update(&mut self){
self.life -= 1;
println!(“{}’s life remain {}”, self.name, self.life);
}
fn is_died(&self)->bool {
self.life <= 0
}
fn spawn(&self)->Box<dyn Task>{
println!(“UnitA spawn UnitB”);
Box::new(UnitB::new())
}
}

一通りの定義が出来たらこれらを可変長配列のVec<T>でインスタンスを保持するように書きます。

let mut units: Vec<Box<dyn Task>> = vec![
Box::new(UnitA::new()),
Box::new(UnitB::new())
];

大事なのはBoxを使ってヒープにデーターを格納します。
直接Vec<dyn Task>で定義するとコンパイルエラーです。
まずはこの辺でどうして?という気持ちになってきます。

そして初期データーの設定が出来たら
ユニット毎に更新を掛けたあとに生き死にチェックをして、死亡していたら死亡後の後継ユニットを作成して配列から取り除くというのをやって見ます。

ここを以下の様に書くと

let unit = units[i].as_mut();
unit.update();
if unit.is_died(){
println!(“unit is died remove”);
units.push(unit.spawn());
units.remove(i);
}
else{
i += 1;
}

以下の様に怒られます

let unit = units[i].as_mut();
—– first mutable borrow occurs here

units.push(unit.spawn());
^^^^^^^^^^^————^
| |
| first borrow later used here
second mutable borrow occurs here

Rustさんの真骨頂の借用チェッカーが仕事をしてくれています。
プログラム的にはunitからspawn()したものをunitsにpushしていると記述しているつもりですがrustの文法的には違うのかもしれません。どうしてそうなのか理解していないです。😋
これの対策として別のVecに追加変数作ったりもしましたが、最終的に記述順番で対処しました、この辺が分かるまでちょっと時間かかりました。
(因みにif unit.is_died(){をC++のノリでif(unit.is_died()){と書くとコンパイラが警告を発生させてくれます)

そして一通り出来たので実行してみます。
意図したとおりに動きました、一安心です。

今回一件意図したように動いているように見えますが実は問題があります。
Boxを使って単一の参照のみで動作させる場合はこれで良いのですが、
実際のゲームでは他の要素を参照したり、そこに変更を加える必要が出てきます。
そう言ったことをやる為にリファレンスカウンター等に手を出していくと
どんどんとコンパイルエラーが発生していきます😥。
今回はその辺まで欲張ろうとしたらごちゃごちゃしてきたので入門程度に簡単にまとめました。

入門レベルでも予期せぬコンパイルエラーによってrustの安全性が伝わってきますね。
これ以上のゲーム向けの作りにするにはC++での作り方の感覚から変えていく必要がありそうです。
次回はこの辺やマルチスレッド化に触れたいと考えています。では。👋

RECRUIT

大阪・東京共にスタッフを募集しています。
特にキャリア採用のプログラマー・アーティストに興味がある方は下のボタンをクリックしてください

RECRUIT SITE