Repository: Ralith/hecs
Pull request #6 (2019-12-15 19:51:36)

Narrow component borrow scope to per-archetype
Allows increased concurrency in diverse worlds.

cc @Ratysz for review


Patch:
diff --git a/macros/src/lib.rs b/macros/src/lib.rs
index 0e63d0c..5b109bf 100644
--- a/macros/src/lib.rs
+++ b/macros/src/lib.rs
@@ -109,105 +109,6 @@ pub fn derive_bundle(input: TokenStream) -> TokenStream {
     TokenStream::from(code)
 }
 
-/// Implement `Query` for a struct whose fields are queries
-///
-/// The struct must have exactly one lifetime parameter, used for the queries.
-#[proc_macro_derive(Query)]
-pub fn derive_query(input: TokenStream) -> TokenStream {
-    let input = parse_macro_input!(input as DeriveInput);
-    let lifetimes = input.generics.lifetimes().collect::<Vec<_>>();
-
-    let lifetime = match lifetimes[..] {
-        [x] => x.lifetime.clone(),
-        _ => {
-            return TokenStream::from(
-                quote! { compile_error!("derive(Query) must be applied to structs with exactly one unbounded lifetime parameter"); },
-            );
-        }
-    };
-    if input.generics.where_clause.is_some() {
-        return TokenStream::from(
-            quote! { compile_error!("derive(Query) does not support where clauses"); },
-        );
-    }
-    let data = match input.data {
-        syn::Data::Struct(s) => s,
-        _ => {
-            return TokenStream::from(
-                quote! { compile_error!("derive(Query) only supports structs"); },
-            )
-        }
-    };
-    let ident = input.ident;
-    let vis = input.vis;
-    let fetch = syn::Ident::new(&format!("{}Fetch", ident), Span::call_site());
-
-    let (tys, fields) = struct_fields(&data.fields);
-
-    let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
-
-    let fetch_def = match data.fields {
-        syn::Fields::Named(_) => quote! {
-            #[doc(hidden)]
-            #vis struct #fetch #ty_generics #where_clause {
-                #(
-                    #fields: <#tys as Query<#lifetime>>::Fetch,
-                )*
-            }
-        },
-        syn::Fields::Unnamed(_) => quote! {
-            #[doc(hidden)]
-            #vis struct #fetch #ty_generics (
-                #(
-                    #fields: <#tys as Query<#lifetime>>::Fetch,
-                )*
-            ) #where_clause;
-        },
-        syn::Fields::Unit => quote! { struct #fetch #ty_generics #where_clause {} },
-    };
-
-    let code = quote! {
-        #fetch_def
-
-        impl #impl_generics ::hecs::Fetch<#lifetime> for #fetch #ty_generics #where_clause {
-            type Item = #ident #ty_generics;
-
-            fn get(archetype: & #lifetime Archetype) -> Option<Self> {
-                Some(Self {
-                    #(
-                        #fields: <#tys as Query<#lifetime>>::Fetch::get(archetype)?,
-                    )*
-                })
-            }
-
-            unsafe fn next(&mut self) -> Self::Item {
-                #ident {
-                    #(
-                        #fields: self.#fields.next(),
-                    )*
-                }
-            }
-        }
-
-        impl #impl_generics ::hecs::Query<#lifetime> for #ident #ty_generics #where_clause {
-            type Fetch = #fetch #ty_generics;
-
-            fn borrow(state: &BorrowState) {
-                #(
-                    <#tys as Query>::borrow(state);
-                )*
-            }
-
-            fn release(state: &BorrowState) {
-                #(
-                    <#tys as Query>::release(state);
-                )*
-            }
-        }
-    };
-    TokenStream::from(code)
-}
-
 fn struct_fields(fields: &syn::Fields) -> (Vec<&syn::Type>, Vec<syn::Ident>) {
     match fields {
         syn::Fields::Named(ref fields) => fields
diff --git a/src/archetype.rs b/src/archetype.rs
index 6c58e4a..de6793c 100644
--- a/src/archetype.rs
+++ b/src/archetype.rs
@@ -15,19 +15,20 @@
 use crate::alloc::alloc::{alloc, dealloc, Layout};
 use crate::alloc::boxed::Box;
 use crate::alloc::{vec, vec::Vec};
-use core::any::TypeId;
+use core::any::{type_name, TypeId};
 use core::cell::UnsafeCell;
 use core::mem;
 use core::ptr::{self, NonNull};
 
 use hashbrown::HashMap;
 
+use crate::borrow::AtomicBorrow;
 use crate::Component;
 
 /// A collection of entities having the same component types
 pub struct Archetype {
     types: Vec<TypeInfo>,
-    offsets: HashMap<TypeId, usize>,
+    state: HashMap<TypeId, TypeState>,
     len: u32,
     entities: Box<[u32]>,
     // UnsafeCell allows unique references into `data` to be constructed while shared references
@@ -44,7 +45,7 @@ impl Archetype {
         );
         Self {
             types,
-            offsets: HashMap::default(),
+            state: HashMap::default(),
             entities: Box::new([]),
             len: 0,
             data: UnsafeCell::new(NonNull::dangling()),
@@ -67,13 +68,51 @@ impl Archetype {
         self.len = 0;
     }
 
-    pub(crate) fn data<T: Component>(&self) -> Option<NonNull<T>> {
-        let offset = *self.offsets.get(&TypeId::of::<T>())?;
+    pub(crate) fn has<T: Component>(&self) -> bool {
+        self.state.contains_key(&TypeId::of::<T>())
+    }
+
+    pub(crate) fn get<T: Component>(&self) -> Option<NonNull<T>> {
+        let state = self.state.get(&TypeId::of::<T>())?;
         Some(unsafe {
-            NonNull::new_unchecked((*self.data.get()).as_ptr().add(offset).cast::<T>() as *mut T)
+            NonNull::new_unchecked(
+                (*self.data.get()).as_ptr().add(state.offset).cast::<T>() as *mut T
+            )
         })
     }
 
+    pub(crate) fn borrow<T: Component>(&self) {
+        if self
+            .state
+            .get(&TypeId::of::<T>())
+            .map_or(false, |x| !x.borrow.borrow())
+        {
+            panic!("{} already borrowed uniquely", type_name::<T>());
+        }
+    }
+
+    pub(crate) fn borrow_mut<T: Component>(&self) {
+        if self
+            .state
+            .get(&TypeId::of::<T>())
+            .map_or(false, |x| !x.borrow.borrow_mut())
+        {
+            panic!("{} already borrowed", type_name::<T>());
+        }
+    }
+
+    pub(crate) fn release<T: Component>(&self) {
+        if let Some(x) = self.state.get(&TypeId::of::<T>()) {
+            x.borrow.release();
+        }
+    }
+
+    pub(crate) fn release_mut<T: Component>(&self) {
+        if let Some(x) = self.state.get(&TypeId::of::<T>()) {
+            x.borrow.release_mut();
+        }
+    }
+
     pub(crate) fn len(&self) -> usize {
         self.len as usize
     }
@@ -90,14 +129,6 @@ impl Archetype {
         &self.types
     }
 
-    /// `index` must be in-bounds
-    pub(crate) unsafe fn get<T: Component>(&self, index: u32) -> Option<NonNull<T>> {
-        debug_assert!(index < self.len);
-        Some(NonNull::new_unchecked(
-            self.data::<T>()?.as_ptr().add(index as usize),
-        ))
-    }
-
     /// `index` must be in-bounds
     pub(crate) unsafe fn get_dynamic(
         &self,
@@ -109,7 +140,7 @@ impl Archetype {
         Some(NonNull::new_unchecked(
             (*self.data.get())
                 .as_ptr()
-                .add(*self.offsets.get(&ty)? + size * index as usize)
+                .add(self.state.get(&ty)?.offset + size * index as usize)
                 .cast::<u8>(),
         ))
     }
@@ -130,10 +161,10 @@ impl Archetype {
         self.entities = new_entities;
 
         let old_data_size = mem::replace(&mut self.data_size, 0);
-        let mut offsets = HashMap::with_capacity_and_hasher(self.types.len(), Default::default());
+        let mut state = HashMap::with_capacity(self.types.len());
         for ty in &self.types {
             self.data_size = align(self.data_size, ty.layout.align());
-            offsets.insert(ty.id, self.data_size);
+            state.insert(ty.id, TypeState::new(self.data_size));
             self.data_size += ty.layout.size() * count;
         }
         let new_data = if self.data_size == 0 {
@@ -150,8 +181,8 @@ impl Archetype {
         };
         if old_data_size != 0 {
             for ty in &self.types {
-                let old_off = *self.offsets.get(&ty.id).unwrap();
-                let new_off = *offsets.get(&ty.id).unwrap();
+                let old_off = self.state.get(&ty.id).unwrap().offset;
+                let new_off = state.get(&ty.id).unwrap().offset;
                 ptr::copy_nonoverlapping(
                     (*self.data.get()).as_ptr().add(old_off),
                     new_data.as_ptr().add(new_off),
@@ -161,7 +192,7 @@ impl Archetype {
         }
 
         self.data = UnsafeCell::new(new_data);
-        self.offsets = offsets;
+        self.state = state;
         self.entities[self.len as usize] = id;
         self.len += 1;
         self.len - 1
@@ -253,6 +284,20 @@ impl Drop for Archetype {
     }
 }
 
+struct TypeState {
+    offset: usize,
+    borrow: AtomicBorrow,
+}
+
+impl TypeState {
+    fn new(offset: usize) -> Self {
+        Self {
+            offset,
+            borrow: AtomicBorrow::new(),
+        }
+    }
+}
+
 /// Metadata required to store a component
 #[derive(Debug, Copy, Clone)]
 pub struct TypeInfo {
diff --git a/src/borrow.rs b/src/borrow.rs
index e1ad989..81255b2 100644
--- a/src/borrow.rs
+++ b/src/borrow.rs
@@ -12,64 +12,21 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use core::any::{type_name, TypeId};
 use core::ops::{Deref, DerefMut};
 use core::ptr::NonNull;
 use core::sync::atomic::{AtomicUsize, Ordering};
 
-use hashbrown::HashMap;
-
 use crate::archetype::Archetype;
-use crate::world::Component;
-
-/// Tracks which components of a world are borrowed in what ways
-#[derive(Default)]
-pub struct BorrowState {
-    states: HashMap<TypeId, AtomicBorrow>,
-}
+use crate::{Component, MissingComponent};
 
-impl BorrowState {
-    pub(crate) fn ensure(&mut self, ty: TypeId) {
-        self.states.entry(ty).or_insert_with(AtomicBorrow::new);
-    }
-
-    /// Acquire a shared borrow
-    pub fn borrow(&self, ty: TypeId, name: &str) {
-        if self.states.get(&ty).map_or(false, |x| !x.borrow()) {
-            panic!("{} already borrowed uniquely", name);
-        }
-    }
-
-    /// Acquire a unique borrow
-    pub fn borrow_mut(&self, ty: TypeId, name: &str) {
-        if self.states.get(&ty).map_or(false, |x| !x.borrow_mut()) {
-            panic!("{} already borrowed", name);
-        }
-    }
-
-    /// Release a shared borrow
-    pub fn release(&self, ty: TypeId) {
-        if let Some(x) = self.states.get(&ty) {
-            x.release();
-        }
-    }
-
-    /// Release a unique borrow
-    pub fn release_mut(&self, ty: TypeId) {
-        if let Some(x) = self.states.get(&ty) {
-            x.release_mut();
-        }
-    }
-}
-
-struct AtomicBorrow(AtomicUsize);
+pub struct AtomicBorrow(AtomicUsize);
 
 impl AtomicBorrow {
-    const fn new() -> Self {
+    pub const fn new() -> Self {
         Self(AtomicUsize::new(0))
     }
 
-    fn borrow(&self) -> bool {
+    pub fn borrow(&self) -> bool {
         let value = self.0.fetch_add(1, Ordering::Acquire).wrapping_add(1);
         if value == 0 {
             // Wrapped, this borrow is invalid!
@@ -83,18 +40,19 @@ impl AtomicBorrow {
         }
     }
 
-    fn borrow_mut(&self) -> bool {
+    pub fn borrow_mut(&self) -> bool {
         self.0
             .compare_exchange(0, UNIQUE_BIT, Ordering::Acquire, Ordering::Relaxed)
             .is_ok()
     }
 
-    fn release(&self) {
+    pub fn release(&self) {
         let value = self.0.fetch_sub(1, Ordering::Release);
-        debug_assert!(value & UNIQUE_BIT == 0);
+        debug_assert!(value != 0, "unbalanced release");
+        debug_assert!(value & UNIQUE_BIT == 0, "shared release of unique borrow");
     }
 
-    fn release_mut(&self) {
+    pub fn release_mut(&self) {
         self.0.store(0, Ordering::Release);
     }
 }
@@ -104,20 +62,30 @@ const UNIQUE_BIT: usize = !(usize::max_value() >> 1);
 /// Shared borrow of an entity's component
 #[derive(Clone)]
 pub struct Ref<'a, T: Component> {
-    borrow: &'a BorrowState,
+    archetype: &'a Archetype,
     target: NonNull<T>,
 }
 
 impl<'a, T: Component> Ref<'a, T> {
-    pub(crate) unsafe fn new(borrow: &'a BorrowState, target: NonNull<T>) -> Self {
-        borrow.borrow(TypeId::of::<T>(), type_name::<T>());
-        Self { borrow, target }
+    pub(crate) unsafe fn new(
+        archetype: &'a Archetype,
+        index: u32,
+    ) -> Result<Self, MissingComponent> {
+        let target = NonNull::new_unchecked(
+            archetype
+                .get::<T>()
+                .ok_or_else(MissingComponent::new::<T>)?
+                .as_ptr()
+                .add(index as usize),
+        );
+        archetype.borrow::<T>();
+        Ok(Self { archetype, target })
     }
 }
 
 impl<'a, T: Component> Drop for Ref<'a, T> {
     fn drop(&mut self) {
-        self.borrow.release(TypeId::of::<T>());
+        self.archetype.release::<T>();
     }
 }
 
@@ -130,20 +98,30 @@ impl<'a, T: Component> Deref for Ref<'a, T> {
 
 /// Unique borrow of an entity's component
 pub struct RefMut<'a, T: Component> {
-    borrow: &'a BorrowState,
+    archetype: &'a Archetype,
     target: NonNull<T>,
 }
 
 impl<'a, T: Component> RefMut<'a, T> {
-    pub(crate) fn new(borrow: &'a BorrowState, target: NonNull<T>) -> Self {
-        borrow.borrow_mut(TypeId::of::<T>(), type_name::<T>());
-        Self { borrow, target }
+    pub(crate) unsafe fn new(
+        archetype: &'a Archetype,
+        index: u32,
+    ) -> Result<Self, MissingComponent> {
+        let target = NonNull::new_unchecked(
+            archetype
+                .get::<T>()
+                .ok_or_else(MissingComponent::new::<T>)?
+                .as_ptr()
+                .add(index as usize),
+        );
+        archetype.borrow_mut::<T>();
+        Ok(Self { archetype, target })
     }
 }
 
 impl<'a, T: Component> Drop for RefMut<'a, T> {
     fn drop(&mut self) {
-        self.borrow.release_mut(TypeId::of::<T>());
+        self.archetype.release_mut::<T>();
     }
 }
 
@@ -163,31 +141,27 @@ impl<'a, T: Component> DerefMut for RefMut<'a, T> {
 /// Handle to an entity with any component types
 #[derive(Copy, Clone)]
 pub struct EntityRef<'a> {
-    borrow: &'a BorrowState,
     archetype: &'a Archetype,
     index: u32,
 }
 
 impl<'a> EntityRef<'a> {
-    pub(crate) fn new(borrow: &'a BorrowState, archetype: &'a Archetype, index: u32) -> Self {
-        Self {
-            borrow,
-            archetype,
-            index,
-        }
+    pub(crate) unsafe fn new(archetype: &'a Archetype, index: u32) -> Self {
+        Self { archetype, index }
     }
 
     /// Borrow the component of type `T`, if it exists
     ///
-    /// Panics if a component of type `T` is already uniquely borrowed from the world
+    /// Panics if the component is already uniquely borrowed from another entity with the same
+    /// components.
     pub fn get<T: Component>(&self) -> Option<Ref<'a, T>> {
-        Some(unsafe { Ref::new(self.borrow, self.archetype.get(self.index)?) })
+        Some(unsafe { Ref::new(self.archetype, self.index).ok()? })
     }
 
     /// Uniquely borrow the component of type `T`, if it exists
     ///
-    /// Panics if a component of type `T` is already borrowed from the world
+    /// Panics if the component is already borrowed from another entity with the same components.
     pub fn get_mut<T: Component>(&self) -> Option<RefMut<'a, T>> {
-        Some(unsafe { RefMut::new(self.borrow, self.archetype.get(self.index)?) })
+        Some(unsafe { RefMut::new(self.archetype, self.index).ok()? })
     }
 }
diff --git a/src/lib.rs b/src/lib.rs
index 45add2e..8fea7dd 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -29,7 +29,7 @@
 //! let mut world = World::new();
 //! let a = world.spawn((123, true, "abc"));
 //! let b = world.spawn((42, false));
-//! for (id, (number, &flag)) in world.query::<(&mut i32, &bool)>() {
+//! for (id, (number, &flag)) in &mut world.query::<(&mut i32, &bool)>() {
 //!   if flag { *number *= 2; }
 //! }
 //! assert_eq!(*world.get::<i32>(a).unwrap(), 246);
@@ -69,14 +69,12 @@ mod world;
 pub use borrow::{EntityRef, Ref, RefMut};
 pub use bundle::{Bundle, DynamicBundle, MissingComponent};
 pub use entity_builder::{BuiltEntity, EntityBuilder};
-pub use query::{Query, QueryIter};
+pub use query::{Query, QueryBorrow, QueryIter};
 pub use world::{Component, ComponentError, Entity, Iter, NoSuchEntity, World};
 
 // Unstable implementation details needed by the macros
 #[doc(hidden)]
 pub use archetype::{Archetype, TypeInfo};
-#[doc(hidden)]
-pub use borrow::BorrowState;
 #[cfg(feature = "macros")]
 #[doc(hidden)]
 pub use lazy_static;
@@ -84,4 +82,4 @@ pub use lazy_static;
 pub use query::Fetch;
 
 #[cfg(feature = "macros")]
-pub use hecs_macros::{Bundle, Query};
+pub use hecs_macros::Bundle;
diff --git a/src/query.rs b/src/query.rs
index 12d26e8..971bbbe 100644
--- a/src/query.rs
+++ b/src/query.rs
@@ -12,43 +12,39 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-use core::any::{type_name, TypeId};
 use core::marker::PhantomData;
 use core::ptr::NonNull;
 
 use crate::archetype::Archetype;
-use crate::borrow::BorrowState;
 use crate::world::EntityMeta;
 use crate::{Component, Entity};
 
 /// A collection of component types to fetch from a `World`
-pub trait Query<'a>: Sized {
-    /// Helper used to process the query
-    type Fetch: Fetch<'a, Item = Self>;
-    /// Dynamically borrow the component types to be accessed, or panic if borrows can't be acquired
-    fn borrow(state: &BorrowState);
-    /// Release dynamic borrows
-    fn release(state: &BorrowState);
+pub trait Query {
+    #[doc(hidden)]
+    type Fetch: for<'a> Fetch<'a>;
 }
 
 /// Streaming iterators over contiguous homogeneous ranges of components
 pub trait Fetch<'a>: Sized {
     /// Type of value to be fetched
     type Item;
+    /// Whether `get` will borrow from `archetype`
+    fn wants(archetype: &Archetype) -> bool;
+    /// Whether `get` will uniquely borrow from `archetype`. Implies `wants`.
+    fn wants_mut(archetype: &Archetype) -> bool;
+    /// Acquire dynamic borrows from `archetype`
+    fn borrow(archetype: &Archetype);
     /// Construct a `Fetch` for `archetype` if it should be traversed
     fn get(archetype: &'a Archetype) -> Option<Self>;
+    /// Release dynamic borrows acquired by `borrow`
+    fn release(archetype: &Archetype);
     /// Access the next item in this archetype without bounds checking
     unsafe fn next(&mut self) -> Self::Item;
 }
 
-impl<'a, T: Component> Query<'a> for &'a T {
+impl<'a, T: Component> Query for &'a T {
     type Fetch = FetchRead<T>;
-    fn borrow(state: &BorrowState) {
-        state.borrow(TypeId::of::<T>(), type_name::<T>())
-    }
-    fn release(state: &BorrowState) {
-        state.release(TypeId::of::<T>())
-    }
 }
 
 #[doc(hidden)]
@@ -56,8 +52,20 @@ pub struct FetchRead<T>(NonNull<T>);
 
 impl<'a, T: Component> Fetch<'a> for FetchRead<T> {
     type Item = &'a T;
+    fn wants(archetype: &Archetype) -> bool {
+        archetype.has::<T>()
+    }
+    fn wants_mut(_: &Archetype) -> bool {
+        false
+    }
+    fn borrow(archetype: &Archetype) {
+        archetype.borrow::<T>();
+    }
     fn get(archetype: &'a Archetype) -> Option<Self> {
-        archetype.data::<T>().map(Self)
+        archetype.get::<T>().map(Self)
+    }
+    fn release(archetype: &Archetype) {
+        archetype.release::<T>();
     }
     unsafe fn next(&mut self) -> &'a T {
         let x = self.0.as_ptr();
@@ -66,14 +74,8 @@ impl<'a, T: Component> Fetch<'a> for FetchRead<T> {
     }
 }
 
-impl<'a, T: Component> Query<'a> for &'a mut T {
+impl<'a, T: Component> Query for &'a mut T {
     type Fetch = FetchWrite<T>;
-    fn borrow(state: &BorrowState) {
-        state.borrow_mut(TypeId::of::<T>(), type_name::<T>())
-    }
-    fn release(state: &BorrowState) {
-        state.release_mut(TypeId::of::<T>())
-    }
 }
 
 #[doc(hidden)]
@@ -81,8 +83,20 @@ pub struct FetchWrite<T>(NonNull<T>);
 
 impl<'a, T: Component> Fetch<'a> for FetchWrite<T> {
     type Item = &'a mut T;
+    fn wants(archetype: &Archetype) -> bool {
+        archetype.has::<T>()
+    }
+    fn wants_mut(archetype: &Archetype) -> bool {
+        archetype.has::<T>()
+    }
+    fn borrow(archetype: &Archetype) {
+        archetype.borrow_mut::<T>();
+    }
     fn get(archetype: &'a Archetype) -> Option<Self> {
-        archetype.data::<T>().map(Self)
+        archetype.get::<T>().map(Self)
+    }
+    fn release(archetype: &Archetype) {
+        archetype.release_mut::<T>();
     }
     unsafe fn next(&mut self) -> &'a mut T {
         let x = self.0.as_ptr();
@@ -91,14 +105,8 @@ impl<'a, T: Component> Fetch<'a> for FetchWrite<T> {
     }
 }
 
-impl<'a, T: Query<'a>> Query<'a> for Option<T> {
+impl<T: Query> Query for Option<T> {
     type Fetch = TryFetch<T::Fetch>;
-    fn borrow(state: &BorrowState) {
-        T::borrow(state);
-    }
-    fn release(state: &BorrowState) {
-        T::release(state);
-    }
 }
 
 #[doc(hidden)]
@@ -106,108 +114,147 @@ pub struct TryFetch<T>(Option<T>);
 
 impl<'a, T: Fetch<'a>> Fetch<'a> for TryFetch<T> {
     type Item = Option<T::Item>;
+    fn wants(archetype: &Archetype) -> bool {
+        T::wants(archetype)
+    }
+    fn wants_mut(archetype: &Archetype) -> bool {
+        T::wants_mut(archetype)
+    }
+    fn borrow(archetype: &Archetype) {
+        T::borrow(archetype)
+    }
     fn get(archetype: &'a Archetype) -> Option<Self> {
         Some(Self(T::get(archetype)))
     }
+    fn release(archetype: &Archetype) {
+        T::release(archetype)
+    }
     unsafe fn next(&mut self) -> Option<T::Item> {
         Some(self.0.as_mut()?.next())
     }
 }
 
-/// Iterator over the set of entities with the components in `Q`
-pub struct QueryIter<'a, Q: Query<'a>> {
-    borrows: &'a BorrowState,
-    meta: &'a [EntityMeta],
-    archetypes: core::slice::Iter<'a, Archetype>,
-    iter: Option<ChunkIter<'a, Q::Fetch>>,
-}
-
-impl<'a, Q: Query<'a>> QueryIter<'a, Q> {
-    pub(crate) fn new(
-        borrows: &'a BorrowState,
-        meta: &'a [EntityMeta],
-        archetypes: &'a [Archetype],
-    ) -> Self {
-        Q::borrow(borrows);
+/// A borrow of a `World` sufficient to execute the query `Q`
+pub struct QueryBorrow<'w, Q: Query> {
+    meta: &'w [EntityMeta],
+    archetypes: &'w [Archetype],
+    _marker: PhantomData<Q>,
+}
+
+impl<'w, Q: Query> QueryBorrow<'w, Q> {
+    pub(crate) fn new(meta: &'w [EntityMeta], archetypes: &'w [Archetype]) -> Self {
+        for x in archetypes {
+            // TODO: Release prior borrows on failure?
+            Q::Fetch::borrow(x);
+        }
         Self {
-            borrows,
             meta,
-            archetypes: archetypes.iter(),
+            archetypes,
+            _marker: PhantomData,
+        }
+    }
+
+    /// Execute the query
+    pub fn iter<'q>(&'q mut self) -> QueryIter<'q, 'w, Q> {
+        QueryIter {
+            borrow: self,
+            archetype_index: 0,
             iter: None,
         }
     }
 }
 
-impl<'a, Q: Query<'a>> Drop for QueryIter<'a, Q> {
+impl<'w, Q: Query> Drop for QueryBorrow<'w, Q> {
     fn drop(&mut self) {
-        Q::release(self.borrows);
+        for x in self.archetypes {
+            Q::Fetch::release(x);
+        }
+    }
+}
+
+impl<'q, 'w, Q: Query> IntoIterator for &'q mut QueryBorrow<'w, Q> {
+    type Item = (Entity, <Q::Fetch as Fetch<'q>>::Item);
+    type IntoIter = QueryIter<'q, 'w, Q>;
+
+    fn into_iter(self) -> Self::IntoIter {
+        self.iter()
     }
 }
 
-impl<'a, Q: Query<'a>> Iterator for QueryIter<'a, Q> {
-    type Item = (Entity, Q);
-    #[inline]
+struct ChunkIter<Q: Query> {
+    entities: NonNull<u32>,
+    fetch: Q::Fetch,
+    len: usize,
+}
+
+/// Iterator over the set of entities with the components in `Q`
+pub struct QueryIter<'q, 'w, Q: Query> {
+    borrow: &'q mut QueryBorrow<'w, Q>,
+    archetype_index: u32,
+    iter: Option<ChunkIter<Q>>,
+}
+
+impl<'q, 'w, Q: Query> Iterator for QueryIter<'q, 'w, Q> {
+    type Item = (Entity, <Q::Fetch as Fetch<'q>>::Item);
     fn next(&mut self) -> Option<Self::Item> {
         loop {
             match self.iter {
                 None => {
-                    let archetype = self.archetypes.next()?;
+                    let archetype = self.borrow.archetypes.get(self.archetype_index as usize)?;
+                    self.archetype_index += 1;
                     self.iter = Q::Fetch::get(archetype).map(|fetch| ChunkIter {
                         entities: archetype.entities(),
                         fetch,
                         len: archetype.len(),
-                        _marker: PhantomData,
                     });
                 }
-                Some(ref mut iter) => match iter.next() {
-                    None => {
+                Some(ref mut iter) => {
+                    if iter.len == 0 {
                         self.iter = None;
+                        continue;
                     }
-                    Some((id, item)) => {
+                    iter.len -= 1;
+                    let entity = iter.entities.as_ptr();
+                    unsafe {
+                        iter.entities = NonNull::new_unchecked(entity.add(1));
                         return Some((
                             Entity {
-                                id,
-                                generation: self.meta[id as usize].generation,
+                                id: *entity,
+                                generation: self.borrow.meta[*entity as usize].generation,
                             },
-                            item,
+                            iter.fetch.next(),
                         ));
                     }
-                },
+                }
             }
         }
     }
 }
 
-struct ChunkIter<'a, T: Fetch<'a>> {
-    entities: NonNull<u32>,
-    fetch: T,
-    len: usize,
-    _marker: PhantomData<&'a ()>,
-}
-
-impl<'a, T: Fetch<'a>> Iterator for ChunkIter<'a, T> {
-    type Item = (u32, T::Item);
-    fn next(&mut self) -> Option<Self::Item> {
-        if self.len == 0 {
-            return None;
-        }
-        self.len -= 1;
-        let entity = self.entities.as_ptr();
-        unsafe {
-            self.entities = NonNull::new_unchecked(entity.add(1));
-            Some((*entity, self.fetch.next()))
-        }
-    }
-}
-
 macro_rules! tuple_impl {
     ($($name: ident),*) => {
         impl<'a, $($name: Fetch<'a>),*> Fetch<'a> for ($($name,)*) {
             type Item = ($($name::Item,)*);
             #[allow(unused_variables)]
+            fn wants(archetype: &Archetype) -> bool {
+                $($name::wants(archetype) &&)* true
+            }
+            #[allow(unused_variables)]
+            fn wants_mut(archetype: &Archetype) -> bool {
+                $($name::wants_mut(archetype) &&)* true
+            }
+            #[allow(unused_variables)]
+            fn borrow(archetype: &Archetype) {
+                $($name::borrow(archetype);)*
+            }
+            #[allow(unused_variables)]
             fn get(archetype: &'a Archetype) -> Option<Self> {
                 Some(($($name::get(archetype)?,)*))
             }
+            #[allow(unused_variables)]
+            fn release(archetype: &Archetype) {
+                $($name::release(archetype);)*
+            }
             unsafe fn next(&mut self) -> Self::Item {
                 #[allow(non_snake_case)]
                 let ($($name,)*) = self;
@@ -215,17 +262,11 @@ macro_rules! tuple_impl {
             }
         }
 
-        #[allow(unused_variables)]
-        impl<'a, $($name: Query<'a>),*> Query<'a> for ($($name,)*) {
+        impl<$($name: Query),*> Query for ($($name,)*) {
             type Fetch = ($($name::Fetch,)*);
-            fn borrow(state: &BorrowState) {
-                $($name::borrow(state);)*
-            }
-            fn release(state: &BorrowState) {
-                $($name::release(state);)*
-            }
         }
-    }
+    };
 }
 
+//smaller_tuples_too!(tuple_impl, B, A);
 smaller_tuples_too!(tuple_impl, O, N, M, L, K, J, I, H, G, F, E, D, C, B, A);
diff --git a/src/world.rs b/src/world.rs
index 1edc2b5..8fb8001 100644
--- a/src/world.rs
+++ b/src/world.rs
@@ -22,8 +22,7 @@ use std::error::Error;
 use hashbrown::{HashMap, HashSet};
 
 use crate::archetype::Archetype;
-use crate::borrow::BorrowState;
-use crate::{Bundle, DynamicBundle, EntityRef, MissingComponent, Query, QueryIter, Ref, RefMut};
+use crate::{Bundle, DynamicBundle, EntityRef, MissingComponent, Query, QueryBorrow, Ref, RefMut};
 
 /// An unordered collection of entities, each having any number of distinctly typed components
 ///
@@ -37,7 +36,6 @@ pub struct World {
     entities: Entities,
     index: HashMap<Vec<TypeId>, u32>,
     archetypes: Vec<Archetype>,
-    borrows: BorrowState,
 }
 
 impl World {
@@ -65,9 +63,6 @@ impl World {
         let entity = self.entities.alloc();
         let archetype = components.with_ids(|ids| {
             self.index.get(ids).copied().unwrap_or_else(|| {
-                for &id in ids {
-                    self.borrows.ensure(id);
-                }
                 let x = self.archetypes.len() as u32;
                 self.archetypes.push(Archetype::new(components.type_info()));
                 self.index.insert(ids.to_vec(), x);
@@ -120,8 +115,11 @@ impl World {
     ///
     /// Entities are yielded in arbitrary order.
     ///
-    /// Query types can also be constructed with `#[derive(Query)]` on a struct whose fields all
-    /// have query types.
+    /// Constructing a query will panic if it would violate an existing unique reference or
+    /// construct an invalid unique reference. This occurs when two simultaneously-active queries
+    /// could expose the same entity. Simultaneous queries can access the same component type if and
+    /// only if the world contains no entities that have all components required by both queries,
+    /// assuming no other component borrows are outstanding.
     ///
     /// # Example
     /// ```
@@ -130,49 +128,49 @@ impl World {
     /// let a = world.spawn((123, true, "abc"));
     /// let b = world.spawn((456, false));
     /// let c = world.spawn((42, "def"));
-    /// let entities = world.query::<(&i32, &bool)>().collect::<Vec<_>>();
+    /// let entities = world.query::<(&i32, &bool)>()
+    ///     .iter()
+    ///     .map(|(e, (&i, &b))| (e, i, b)) // Copy out of the world
+    ///     .collect::<Vec<_>>();
     /// assert_eq!(entities.len(), 2);
-    /// assert!(entities.contains(&(a, (&123, &true))));
-    /// assert!(entities.contains(&(b, (&456, &false))));
+    /// assert!(entities.contains(&(a, 123, true)));
+    /// assert!(entities.contains(&(b, 456, false)));
     /// ```
-    pub fn query<'a, Q: Query<'a>>(&'a self) -> QueryIter<'a, Q> {
-        QueryIter::new(&self.borrows, &self.entities.meta, &self.archetypes)
+    pub fn query<Q: Query>(&self) -> QueryBorrow<'_, Q> {
+        QueryBorrow::new(&self.entities.meta, &self.archetypes)
     }
 
     /// Borrow the `T` component of `entity`
     ///
-    /// Panics if the component is already uniquely borrowed.
+    /// Panics if the component is already uniquely borrowed from another entity with the same
+    /// components.
     pub fn get<T: Component>(&self, entity: Entity) -> Result<Ref<'_, T>, ComponentError> {
         let meta = &self.entities.meta[entity.id as usize];
         if meta.generation != entity.generation {
             return Err(ComponentError::NoSuchEntity);
         }
-        unsafe {
-            Ok(Ref::new(
-                &self.borrows,
-                self.archetypes[meta.location.archetype as usize]
-                    .get(meta.location.index)
-                    .ok_or_else(MissingComponent::new::<T>)?,
-            ))
-        }
+        Ok(unsafe {
+            Ref::new(
+                &self.archetypes[meta.location.archetype as usize],
+                meta.location.index,
+            )?
+        })
     }
 
     /// Uniquely borrow the `T` component of `entity`
     ///
-    /// Panics if the component is already borrowed.
+    /// Panics if the component is already borrowed from another entity with the same components.
     pub fn get_mut<T: Component>(&self, entity: Entity) -> Result<RefMut<'_, T>, ComponentError> {
         let meta = &self.entities.meta[entity.id as usize];
         if meta.generation != entity.generation {
             return Err(ComponentError::NoSuchEntity);
         }
-        unsafe {
-            Ok(RefMut::new(
-                &self.borrows,
-                self.archetypes[meta.location.archetype as usize]
-                    .get(meta.location.index)
-                    .ok_or_else(MissingComponent::new::<T>)?,
-            ))
-        }
+        Ok(unsafe {
+            RefMut::new(
+                &self.archetypes[meta.location.archetype as usize],
+                meta.location.index,
+            )?
+        })
     }
 
     /// Access an entity regardless of its component types
@@ -183,11 +181,12 @@ impl World {
         if meta.generation != entity.generation {
             return Err(NoSuchEntity);
         }
-        Ok(EntityRef::new(
-            &self.borrows,
-            &self.archetypes[meta.location.archetype as usize],
-            meta.location.index,
-        ))
+        Ok(unsafe {
+            EntityRef::new(
+                &self.archetypes[meta.location.archetype as usize],
+                meta.location.index,
+            )
+        })
     }
 
     /// Iterate over all entities in the world
@@ -204,7 +203,7 @@ impl World {
     /// assert_eq!(world.iter().map(|(id, _)| id).collect::<Vec<_>>(), &[a, b]);
     /// ```
     pub fn iter(&self) -> Iter<'_> {
-        Iter::new(&self.borrows, &self.archetypes, &self.entities.meta)
+        Iter::new(&self.archetypes, &self.entities.meta)
     }
 
     /// Add `components` to `entity`
@@ -236,7 +235,6 @@ impl World {
                 if let Some(ptr) = arch.get_dynamic(ty.id(), ty.layout().size(), loc.index) {
                     ty.drop(ptr.as_ptr());
                 } else {
-                    self.borrows.ensure(ty.id());
                     info.push(ty);
                 }
             }
@@ -355,6 +353,24 @@ impl World {
     pub fn remove_one<T: Component>(&mut self, entity: Entity) -> Result<T, ComponentError> {
         self.remove::<(T,)>(entity).map(|(x,)| x)
     }
+
+    // /// Determine which collections of entities will be borrowed by `Q`
+    // ///
+    // /// The identifiers returned by this are invalidated when any entity or component is added or
+    // /// removed.
+    // pub fn query_scope<'a, Q: Query<'a>>(&self) -> impl Iterator<Item = u32> + '_ {
+    //     (0..self.archetypes.len() as u32)
+    //         .filter(move |&i| Q::Fetch::wants(&self.archetypes[i as usize]))
+    // }
+
+    // /// Determine which collections of entities will be uniquely borrowed by `Q`
+    // ///
+    // /// The identifiers returned by this are invalidated when any entity or component is added or
+    // /// removed.
+    // pub fn query_scope_mut<'a, Q: Query<'a>>(&self) -> impl Iterator<Item = u32> + '_ {
+    //     (0..self.archetypes.len() as u32)
+    //         .filter(move |&i| Q::Fetch::wants_mut(&self.archetypes[i as usize]))
+    // }
 }
 
 unsafe impl Sync for World {}
@@ -443,7 +459,6 @@ impl fmt::Debug for Entity {
 
 /// Iterator over all of a world's entities
 pub struct Iter<'a> {
-    borrows: &'a BorrowState,
     archetypes: core::slice::Iter<'a, Archetype>,
     entities: &'a [EntityMeta],
     current: Option<&'a Archetype>,
@@ -451,13 +466,8 @@ pub struct Iter<'a> {
 }
 
 impl<'a> Iter<'a> {
-    fn new(
-        borrows: &'a BorrowState,
-        archetypes: &'a [Archetype],
-        entities: &'a [EntityMeta],
-    ) -> Self {
+    fn new(archetypes: &'a [Archetype], entities: &'a [EntityMeta]) -> Self {
         Self {
-            borrows,
             archetypes: archetypes.iter(),
             entities,
             current: None,
@@ -488,7 +498,7 @@ impl<'a> Iterator for Iter<'a> {
                             id,
                             generation: self.entities[id as usize].generation,
                         },
-                        EntityRef::new(self.borrows, current, index),
+                        unsafe { EntityRef::new(current, index) },
                     ));
                 }
             }


Test patch:
diff --git a/tests/tests.rs b/tests/tests.rs
index ca4a75b..526b9d6 100644
--- a/tests/tests.rs
+++ b/tests/tests.rs
@@ -45,12 +45,16 @@ fn query_all() {
     let e = world.spawn(("abc", 123));
     let f = world.spawn(("def", 456));
 
-    let ents = world.query::<(&i32, &&str)>().collect::<Vec<_>>();
+    let ents = world
+        .query::<(&i32, &&str)>()
+        .iter()
+        .map(|(e, (&i, &s))| (e, i, s))
+        .collect::<Vec<_>>();
     assert_eq!(ents.len(), 2);
-    assert!(ents.contains(&(e, (&123, &"abc"))));
-    assert!(ents.contains(&(f, (&456, &"def"))));
+    assert!(ents.contains(&(e, 123, "abc")));
+    assert!(ents.contains(&(f, 456, "def")));
 
-    let ents = world.query::<()>().collect::<Vec<_>>();
+    let ents = world.query::<()>().iter().collect::<Vec<_>>();
     assert_eq!(ents.len(), 2);
     assert!(ents.contains(&(e, ())));
     assert!(ents.contains(&(f, ())));
@@ -61,10 +65,14 @@ fn query_single_component() {
     let mut world = World::new();
     let e = world.spawn(("abc", 123));
     let f = world.spawn(("def", 456, true));
-    let ents = world.query::<&i32>().collect::<Vec<_>>();
+    let ents = world
+        .query::<&i32>()
+        .iter()
+        .map(|(e, &i)| (e, i))
+        .collect::<Vec<_>>();
     assert_eq!(ents.len(), 2);
-    assert!(ents.contains(&(e, &123)));
-    assert!(ents.contains(&(f, &456)));
+    assert!(ents.contains(&(e, 123)));
+    assert!(ents.contains(&(f, 456)));
 }
 
 #[test]
@@ -72,8 +80,7 @@ fn query_missing_component() {
     let mut world = World::new();
     world.spawn(("abc", 123));
     world.spawn(("def", 456));
-    let ents = world.query::<(&bool, &i32)>().collect::<Vec<_>>();
-    assert_eq!(ents.len(), 0);
+    assert!(world.query::<(&bool, &i32)>().iter().next().is_none());
 }
 
 #[test]
@@ -81,8 +88,12 @@ fn query_sparse_component() {
     let mut world = World::new();
     world.spawn(("abc", 123));
     let f = world.spawn(("def", 456, true));
-    let ents = world.query::<&bool>().collect::<Vec<_>>();
-    assert_eq!(ents, &[(f, &true)]);
+    let ents = world
+        .query::<&bool>()
+        .iter()
+        .map(|(e, &b)| (e, b))
+        .collect::<Vec<_>>();
+    assert_eq!(ents, &[(f, true)]);
 }
 
 #[test]
@@ -90,10 +101,14 @@ fn query_optional_component() {
     let mut world = World::new();
     let e = world.spawn(("abc", 123));
     let f = world.spawn(("def", 456, true));
-    let ents = world.query::<(Option<&bool>, &i32)>().collect::<Vec<_>>();
+    let ents = world
+        .query::<(Option<&bool>, &i32)>()
+        .iter()
+        .map(|(e, (b, &i))| (e, b.copied(), i))
+        .collect::<Vec<_>>();
     assert_eq!(ents.len(), 2);
-    assert!(ents.contains(&(e, (None, &123))));
-    assert!(ents.contains(&(f, (Some(&true), &456))));
+    assert!(ents.contains(&(e, None, 123)));
+    assert!(ents.contains(&(f, Some(true), 456)));
 }
 
 #[test]
@@ -119,14 +134,29 @@ fn dynamic_components() {
     let e = world.spawn((42,));
     world.insert(e, (true, "abc")).unwrap();
     assert_eq!(
-        world.query::<(&i32, &bool)>().collect::<Vec<_>>(),
-        &[(e, (&42, &true))]
+        world
+            .query::<(&i32, &bool)>()
+            .iter()
+            .map(|(e, (&i, &b))| (e, i, b))
+            .collect::<Vec<_>>(),
+        &[(e, 42, true)]
     );
     assert_eq!(world.remove_one::<i32>(e), Ok(42));
-    assert_eq!(world.query::<(&i32, &bool)>().collect::<Vec<_>>(), &[]);
     assert_eq!(
-        world.query::<(&bool, &&str)>().collect::<Vec<_>>(),
-        &[(e, (&true, &"abc"))]
+        world
+            .query::<(&i32, &bool)>()
+            .iter()
+            .map(|(e, (&i, &b))| (e, i, b))
+            .collect::<Vec<_>>(),
+        &[]
+    );
+    assert_eq!(
+        world
+            .query::<(&bool, &&str)>()
+            .iter()
+            .map(|(e, (&b, &s))| (e, b, s))
+            .collect::<Vec<_>>(),
+        &[(e, true, "abc")]
     );
 }
 
@@ -197,30 +227,6 @@ fn bad_bundle_derive() {
     world.spawn(Foo { x: 42, y: 42 });
 }
 
-#[test]
-#[cfg(feature = "macros")]
-fn derived_query() {
-    #[derive(Query, PartialEq)]
-    struct Q<'a> {
-        x: &'a i32,
-        y: Option<&'a bool>,
-    }
-
-    let mut world = World::new();
-    let e = world.spawn((42, true));
-    let f = world.spawn((17,));
-    let ents = world.query::<Q>().collect::<Vec<_>>();
-    assert_eq!(ents.len(), 2);
-    assert!(ents.contains(&(
-        e,
-        Q {
-            x: &42,
-            y: Some(&true),
-        }
-    )));
-    assert!(ents.contains(&(f, Q { x: &17, y: None })));
-}
-
 #[test]
 #[cfg_attr(miri, ignore)]
 fn spawn_many() {