前回、こんなコードを書きました
use rand;
#[derive(Debug)]
struct Position {
x: i32,
y: i32,
}
fn main() {
// idは配列の要素数を表す
let mut id = 0;
// 配列をやめて、ベクタで宣言する
// Position構造体のベクタ
let mut pos_ary: Vec<Position> = vec![];
// 効果を確かめるために、10回 create_entity を呼び出す
for i in 0..10 {
// ランダムな値を取得する
let (x, y) = rand::random();
create_entity(&mut id, &mut pos_ary,);
let t = id -1;
set_data(&t, &mut pos_ary,Position{ x, y });
}
for i in 0..id {
println!("{}: {:?}", i, pos_ary[i])
}
}
// idを増やして、配列に要素を追加する
fn create_entity(id: &mut usize, ary: &mut Vec<Position>){
*id += 1;
ary.push(Position { x: 0, y: 0 });
}
// idを増やして、配列に指定された値の要素を追加する
fn create_entity_pos(id: &mut usize, ary: &mut Vec<Position>, data: Position) {
*id += 1;
ary.push(data);
}
// 配列の指定されたidの要素に、指定された値を代入する
fn set_data (id: &usize, ary: &mut Vec<Position>, data: Position) {
ary[*id] = data;
}
さて、前回に課題とした、コンポーネントが1種類しか無い。に対処します。
なので、コンポーネントの種類を増やしたいと思います。
おさらいですが、コンポーネントは構造体として定義するようにしました。
すなわち、コンポーネントの種類を増やすとは、新しい構造体を定義するということになります。
では、構造体を定義します。
#[derive(Debug)]
struct Velocity {
x: i32,
y: i32,
}
つぎに、Velocity構造体のベクタを宣言します。
// Velocity構造体のベクタ
let mut vel_ary: Vec<Velocity> = vec![];
そして、Velocity構造体に対応した関数を定義します。
fn create_entity_vel_zero_init(id: &mut usize, ary: &mut Vec<Velocity>) {
*id += 1;
ary.push(Velocity { x: 0, y: 0 });
}
fn create_entity_vel(id: &mut usize, ary: &mut Vec<Velocity>, data: Velocity) {
*id += 1;
ary.push(data);
}
fn set_data_vel(id: &usize, ary: &mut Vec<Velocity>, data: Velocity) {
ary[*id] = data;
}
と、ここまで書いて気づく方もいるかと思いますが、Positionのときとほぼ同じ関数になっています。
// idを増やして、配列に要素を追加する
-fn create_entity(id: &mut usize, ary: &mut Vec<Position>){
- *id += 1;
- ary.push(Position { x: 0, y: 0 });
-}
// idを増やして、配列に指定された値の要素を追加する
-fn create_entity_pos(id: &mut usize, ary: &mut Vec<Position>, data: Position) {
- *id += 1;
- ary.push(data);
-}
// 配列の指定されたidの要素に、指定された値を代入する
-fn set_data (id: &usize, ary: &mut Vec<Position>, data: Position) {
- ary[*id] = data;
-}
+fn create_entity_vel_zero_init(id: &mut usize, ary: &mut Vec<Velocity>) {
+ *id += 1;
+ ary.push(Velocity { x: 0, y: 0 });
+}
+fn create_entity_vel(id: &mut usize, ary: &mut Vec<Velocity>, data: Velocity) {
+ *id += 1;
+ ary.push(data);
+}
+fn set_data_vel(id: &usize, ary: &mut Vec<Velocity>, data: Velocity) {
+ ary[*id] = data;
+}
行の先頭が -
になっているのがPositionを引数に取る関数です。
一方で先頭が +
になっているのがVelocityを引数にとる関数です。
違いは、関数の名前と引数の型です。
こんなとき、ジェネリックを使うと、より良いコードが書けます。
では、書きます。
// ジェネリックなデータ型を引数に取る create_entity 関数
fn generic_create_entity <T> (id: &mut usize, v: &mut Vec<T>, data: T) {
v.push(data);
*id += 1;
}
// ジェネリックなデータ型を引数に取る set_data 関数
fn generic_set_data <T> (id: &usize, v: &mut Vec<T>, data: T) {
v[*id] = data;
}
このコードの <T>
は、任意の型を受け入れますよ という意味です。
さっそく、動くか確かめます。
// 効果を確かめるために、10回 create_entity を呼び出す
for i in 0..10 {
// ランダムな値を取得する
let (x, y) = rand::random();
generic_create_entity(&mut id, &mut pos_ary, Position { x, y });
generic_create_entity(&mut id, &mut vel_ary, Velocity { x, y });
}
// ベクタの要素を出力する
for i in 0..id {
println!("{}: {:?}", i, pos_ary[i]);
println!("{}: {:?}", i, vel_ary[i]);
}
結果は、どうでしょうか
0: Position { x: 1348585918, y: -109130012 }
0: Velocity { x: 1348585918, y: -109130012 }
1: Position { x: 1271725324, y: -1159539081 }
1: Velocity { x: 1271725324, y: -1159539081 }
2: Position { x: -1223670204, y: -1138215956 }
2: Velocity { x: -1223670204, y: -1138215956 }
3: Position { x: 941828948, y: 1712899630 }
3: Velocity { x: 941828948, y: 1712899630 }
4: Position { x: -933814175, y: 485824803 }
4: Velocity { x: -933814175, y: 485824803 }
5: Position { x: 230057070, y: -1249036585 }
5: Velocity { x: 230057070, y: -1249036585 }
6: Position { x: -1642141611, y: -1714327873 }
6: Velocity { x: -1642141611, y: -1714327873 }
7: Position { x: 640201263, y: -514722672 }
7: Velocity { x: 640201263, y: -514722672 }
8: Position { x: 596698490, y: 1840968600 }
8: Velocity { x: 596698490, y: 1840968600 }
9: Position { x: 783147762, y: -338984894 }
9: Velocity { x: 783147762, y: -338984894 }
thread 'main' panicked at 'index out of bounds: the len is 10 but the index is 10', src\main.rs:38:33
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\EntitiyCompornentSystem-Rust.exe` (exit code: 101)
一見、うまく動いているようにみえますが、よく読んでみるとエラーが発生しています。
thread 'main' panicked at 'index out of bounds: the len is 10 but the index is 10', src\main.rs:38:33
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
error: process didn't exit successfully: `target\debug\EntitiyCompornentSystem-Rust.exe` (exit code: 101)
エラーメッセージの部分を抜粋しました。
どうやらベクタの範囲外にアクセスしようとしてしまったようです。
解決策は、次回考えます。
ひとまず、エラーを除けば関数は正しく動作しているので、今回はここまでにします。
最後に、コードの全体を記載して終わります。
※不要になった関数は消してあります。
use rand;
#[derive(Debug)]
struct Position {
x: i32,
y: i32,
}
#[derive(Debug)]
struct Velocity {
x: i32,
y: i32,
}
fn main() {
// idは配列の要素数を表す
let mut id = 0;
// 配列をやめて、ベクタで宣言する
// Position構造体のベクタ
let mut pos_ary: Vec<Position> = vec![];
// Velocity構造体のベクタ
let mut vel_ary: Vec<Velocity> = vec![];
// 効果を確かめるために、10回 create_entity を呼び出す
for i in 0..10 {
// ランダムな値を取得する
let (x, y) = rand::random();
generic_create_entity(&mut id, &mut pos_ary, Position { x, y });
generic_create_entity(&mut id, &mut vel_ary, Velocity { x, y });
}
// ベクタの要素を出力する
for i in 0..id {
println!("{}: {:?}", i, pos_ary[i]);
println!("{}: {:?}", i, vel_ary[i]);
}
}
// ジェネリックなデータ型を引数に取る create_entity 関数
fn generic_create_entity <T> (id: &mut usize, v: &mut Vec<T>, data: T) {
v.push(data);
*id += 1;
}
// ジェネリックなデータ型を引数に取る set_data 関数
fn generic_set_data <T> (id: &usize, v: &mut Vec<T>, data: T) {
v[*id] = data;
}