Chapter 11

イテレータ

toyboot 4e
toyboot 4e
2021.12.26に更新

Entity 経由のイテレータを作ります。

ポイントだけメモしたので、何を言っているのか伝わらないかもしれません。実装も酷いものですみません 🙇

Comp<T>CompMut<T> の組からイテレータが作れるようになります:

fn add_system(mut us: CompMut<usize>, is: Comp<isize>, add: Res<usize>) {
    for (u, i) in (&mut us, &is).iter() {
        *u += (-*i) as usize + *add;
    }
}

add_system.run(&world);

なお &CompMut<T>&Comp<T> と同じ扱いとし、 &mut CompMut<T> から区別します。

1 種類の component のイテレータ

こちらは [T]::iter に準じる速いイテレータです。

Comp<T>CompMut<T> を抽象する

trait View を追加しました。 ViewSparseSet<T> を 2 つに分解します:

/// `&Comp<T>` | `&CompMut<T>` | `&mut CompMut<T>`
pub unsafe trait View<'a> {
    type Binding: AnyBinding;
    fn into_parts(self) -> (&'a [Entity], Self::Binding);
}

/// Shorthand
type ViewItem<'a, V> = <<V as View<'a>>::Binding as AnyBinding>::Item;

/// `Binding<&[T]>` | `Binding<&mut [T]>`
///
/// `usize` か `Entity` で中のデータにアクセスできる
pub trait AnyBinding {
    type Item;
    fn get(&mut self, ent: Entity) -> Option<Self::Item>;
    unsafe fn get_by_slot_unchecked(&mut self, slot: usize) -> Self::Item;
}

#[derive(Clone)]
pub struct Binding<'a, Slice> {
    to_dense: &'a [Option<DenseIndex>],
    data: Slice,
}

sparseyshipyard では、そもそも Comp<T>CompMut<T> を同じ struct ComponentView<T> で表しています。

AnyBinding を通じてイテレータを実装します。

Lifetime を誤魔化す

Mutable iterator の実装が難しく、ズルをしました 🙇

// `&'_ mut self` から `&'a mut T` を返すアクセサ
impl<'a, T> AnyBinding for Binding<'a, &'a mut [T]> {
    type Item = &'a mut T;
    // ポインタ経由のキャストで実装
    fn get(&mut self, ent: Entity) -> Option<Self::Item> { /* ~~ * /}
    unsafe fn get_by_slot_unchecked(&mut self, slot: usize) -> Self::Item { /* ~~ * /}
}

miri には怒られなかったので、極端に酷い間違いはしていない……と思いたいです……

(Entity, Item) のイテレータも作れるようにする

sparsey を真似て (&a, &b).iter().entities() と書けるようにしました。

InteIterator は実装できない?

Unconstrained lifetime と言われたのでライフタイム付きの IntoIterator を作りました:

pub trait Iter<'a> {
    type I;
    fn iter(self) -> Self::I;
}

ユーザは必ず .iter() を呼びます。

IntoIterator を実装する良いトリックがあるかもしれませんが、 Bevy の API もこんな感じだったので無理かもしれません。

リファレンス実装: 6b94be6

複数種類の component のイテレータ

SparseIndex 経由のイテレータです。速度は残念ですが、イテレーションの API は完成します。

複数種類の component の高速なイテレーションは『グループ』の章で扱います。

関連型を使ってコンパイルが通らないときは generics を使う

もう Rust 分からないですね……

ゼロコスト具象

Const generics で脳筋しました。

リファレンス実装: fdd3534