bevy/crates/bevy_ecs/src/world/spawn_batch.rs
Jakob Hellermann d38a8dfdd7 add more SAFETY comments and lint for missing ones in bevy_ecs (#4835)
# Objective

`SAFETY` comments are meant to be placed before `unsafe` blocks and should contain the reasoning of why in this case the usage of unsafe is okay. This is useful when reading the code because it makes it clear which assumptions are required for safety, and makes it easier to spot possible unsoundness holes. It also forces the code writer to think of something to write and maybe look at the safety contracts of any called unsafe methods again to double-check their correct usage.

There's a clippy lint called `undocumented_unsafe_blocks` which warns when using a block without such a comment. 

## Solution

- since clippy expects `SAFETY` instead of `SAFE`, rename those
- add `SAFETY` comments in more places
- for the last remaining 3 places, add an `#[allow()]` and `// TODO` since I wasn't comfortable enough with the code to justify their safety
- add ` #![warn(clippy::undocumented_unsafe_blocks)]` to `bevy_ecs`


### Note for reviewers

The first commit only renames `SAFETY` to `SAFE` so it doesn't need a thorough review.
cb042a416e..55cef2d6fa is the diff for all other changes.

### Safety comments where I'm not too familiar with the code

774012ece5/crates/bevy_ecs/src/entity/mod.rs (L540-L546)

774012ece5/crates/bevy_ecs/src/world/entity_ref.rs (L249-L252)

### Locations left undocumented with a `TODO` comment

5dde944a30/crates/bevy_ecs/src/schedule/executor_parallel.rs (L196-L199)

5dde944a30/crates/bevy_ecs/src/world/entity_ref.rs (L287-L289)

5dde944a30/crates/bevy_ecs/src/world/entity_ref.rs (L413-L415)

Co-authored-by: Jakob Hellermann <hellermann@sipgate.de>
2022-07-04 14:44:24 +00:00

94 lines
2 KiB
Rust

use crate::{
bundle::{Bundle, BundleSpawner},
entity::Entity,
world::World,
};
use std::iter::FusedIterator;
pub struct SpawnBatchIter<'w, I>
where
I: Iterator,
I::Item: Bundle,
{
inner: I,
spawner: BundleSpawner<'w, 'w>,
}
impl<'w, I> SpawnBatchIter<'w, I>
where
I: Iterator,
I::Item: Bundle,
{
#[inline]
pub(crate) fn new(world: &'w mut World, iter: I) -> Self {
// Ensure all entity allocations are accounted for so `self.entities` can realloc if
// necessary
world.flush();
let (lower, upper) = iter.size_hint();
let length = upper.unwrap_or(lower);
let bundle_info = world
.bundles
.init_info::<I::Item>(&mut world.components, &mut world.storages);
world.entities.reserve(length as u32);
let mut spawner = bundle_info.get_bundle_spawner(
&mut world.entities,
&mut world.archetypes,
&mut world.components,
&mut world.storages,
*world.change_tick.get_mut(),
);
spawner.reserve_storage(length);
Self {
inner: iter,
spawner,
}
}
}
impl<I> Drop for SpawnBatchIter<'_, I>
where
I: Iterator,
I::Item: Bundle,
{
fn drop(&mut self) {
for _ in self {}
}
}
impl<I> Iterator for SpawnBatchIter<'_, I>
where
I: Iterator,
I::Item: Bundle,
{
type Item = Entity;
fn next(&mut self) -> Option<Entity> {
let bundle = self.inner.next()?;
// SAFETY: bundle matches spawner type
unsafe { Some(self.spawner.spawn(bundle)) }
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.inner.size_hint()
}
}
impl<I, T> ExactSizeIterator for SpawnBatchIter<'_, I>
where
I: ExactSizeIterator<Item = T>,
T: Bundle,
{
fn len(&self) -> usize {
self.inner.len()
}
}
impl<I, T> FusedIterator for SpawnBatchIter<'_, I>
where
I: FusedIterator<Item = T>,
T: Bundle,
{
}