LoginSignup
16
11

More than 5 years have passed since last update.

【Rust】構造体に自身のフィールドの参照の配列を持たせる方法

Posted at

Rustはライフタイムに厳しく、また複雑なため説明が丁寧でも対処法がわかりづらいことが多いです。
私は次のような状況での解決方法がついに分かったため、備忘録としても、また今回のケースにぴったりの解決法が調べても出てこなかったため残します。

簡単な例として、シューティングゲームのエネミーが弾のテクスチャと自身の発射した弾の配列を持っているとします。

struct Bullet<'a>(&'a Texture);

struct Enemy<'a> {
  bullet_texture: Texture,
  bullets: Vec<Bullet<'a>>
}

impl<'a> Enemy<'a> {
  fn new(bullet_texture: Texture) -> Self {
    Enemy { bullet_texture, bullets: vec![] }
  }
  fn shot(&mut self) {
    self.bullets.push(Bullet(&self.bullet_texture));
  }
}

このコードの場合次のようなエラーになります。

error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
  --> src\main.rs:15:30
   |
15 |     self.bullets.push(Bullet(&self.bullet_texture));
   |                              ^^^^^^^^^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 14:3...
  --> src\main.rs:14:3
   |
14 | /   fn shot(&mut self) {
15 | |     self.bullets.push(Bullet(&self.bullet_texture));
16 | |   }
   | |___^
note: ...so that reference does not outlive borrowed content
  --> src\main.rs:15:30
   |
15 |     self.bullets.push(Bullet(&self.bullet_texture));
   |                              ^^^^^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 10:6...
  --> src\main.rs:10:6
   |
10 | impl<'a> Enemy<'a> {
   |      ^^
   = note: ...so that the expression is assignable:
           expected Bullet<'a>
              found Bullet<'_>

.....。なんとなく言ってることはわかるのですが、これを読んでもすぐに対処法が思いつきそうにはありません。

最初、BulletのライフタイムはEnemyと同じかそれより短いので、弾が持つEnemyのフィールドへの参照がより長く生存することはないと思い、このエラーの原因がずっとわかりませんでした。

ただ問題点はそこではなかったようで、次のようにコードを変えるとエラーが出なくなります。

  fn shot(&'a mut self) { // lifetime parameter
    self.bullets.push(Bullet(&self.bullet_texture));
  }

(ここからは自論です。違っているかもしれません。)
先ほどのエラー内容の最初、anonymous lifetime #1という表現がありましたが、これは&mut selfに与えられる参照のライフタイムのことです。
Enemy構造体の定義時にいくつかライフタイムを与えましたが、それらが示すのは、「Enemy.bullets配列が持つ参照のライフタイムとEnemyのライフタイムは同じ」というものでした。
しかし最初のコードではshot()に与えられる参照(&mut self)のライフタイムが明示されていなかったため、&self.bullet_textureのライフタイムとの関係性が不明なままでした。これが原因だと思います。
なので、&'a mut selfとすることで回避できた...ということだと思います。

もっと詳しく解説していただける方がいらっしゃいましたらコメントなどくださると幸いです。

16
11
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
11