A few months ago, I started learning Rust and Bevy. After a few projects my code started being repetitive, and too large to be concise and stay in a single function. To solve this problem I wrote a small macro to generate type aliases for queries and it's items and make passing their references easier.
macro_rules! mut_sys_alias {
(
$type_name:ident, $wq_q:tt, $wq_f:tt
) => {
paste::paste!{
type $type_name <'w, 's> = Query<'w, 's, $wq_q, $wq_f>;
type [<$type_name Item >]<'w> = QueryItem<'w, $wq_q>;
}
};
}
It is used like so:
mut_sys_alias! {
QContainables,
(
&'static mut Transform,
&'static GlobalTransform,
),
(
With<Containable>,
With<Draggable>,
Without<Contained>,
)
}
mut_sys_alias! {
QContained,
(
&'static mut Transform,
&'static GlobalTransform,
&'static mut Contained,
&'static Parent,
),
(
With<Containable>,
With<Draggable>,
)
}
mut_sys_alias! {
QContainers,
(
Entity,
&'static mut Container,
&'static GlobalTransform,
&'static Children,
),
()
}
This is where the problem occures:
pub fn us_container(
mut commands: Commands,
removed_dragging: RemovedComponents<Dragging>,
mut q_containables: QContainables,
mut q_contained: QContained,
mut q_containers: QContainers,
) {
for removed_dragging_entity in removed_dragging.iter() {
// Check if the current dagging entity was in a container previously
if let Ok(mut t_containable) = q_containables.get_mut(removed_dragging_entity) {
// NO it wasn't
// Loop trough all potential containers and see which is the most suitable
for mut t_container in q_containers.iter_mut() {
// Add to container
if util_add_to_container(
&mut commands,
removed_dragging_entity,
&mut t_containable,
&mut t_container,
&mut q_contained,
)
.is_ok()
{
return;
}
}
} else if let Ok(mut t_contained) = q_contained.get_mut(removed_dragging_entity) {
util_remove_from_container(
&mut commands,
removed_dragging_entity,
&mut t_contained,
&mut q_contained,
&mut q_containers,
);
// Previous code
// let (mut contained_tran, contained_gtran, contained_compnent, contained_parent) =
// t_contained;
// if let Ok(t_container) = q_containers.get_mut(contained_parent.get()) {
// let (container_entity, mut container, _container_gtran, container_children) =
// t_container;
// commands
// .entity(removed_dragging_entity)
// .remove::<Contained>();
// contained_tran.translation = contained_gtran.translation();
// commands
// .entity(container_entity)
// .remove_children(&[removed_dragging_entity]);
// container.len -= 1;
// let idx = contained_compnent.idx;
// for child_entity in container_children.iter() {
// if let Ok(t_contained) = q_contained.get_mut(*child_entity) {
// let (_contained_tran, _contained_gtran, mut contained_compnent, _) =
// t_contained;
// if contained_compnent.idx > idx as u8 {
// contained_compnent.idx -= 1;
// }
// }
// }
// }
}
}
}
fn util_remove_from_container(
commands: &mut Commands,
removed_dragging_entity: Entity,
t_contained: &mut QContainedItem,
q_contained: &mut QContained,
q_containers: &mut QContainers,
) {
let (contained_tran, contained_gtran, contained_compnent, contained_parent) = t_contained;
if let Ok(t_container) = q_containers.get_mut(contained_parent.get()) {
let (container_entity, mut container, _container_gtran, container_children) = t_container;
commands
.entity(removed_dragging_entity)
.remove::<Contained>();
contained_tran.translation = contained_gtran.translation();
commands
.entity(container_entity)
.remove_children(&[removed_dragging_entity]);
container.len -= 1;
let idx = contained_compnent.idx;
for child_entity in container_children.iter() {
if let Ok(t_contained) = q_contained.get_mut(*child_entity) {
let (_contained_tran, _contained_gtran, mut contained_compnent, _) = t_contained;
if contained_compnent.idx > idx as u8 {
contained_compnent.idx -= 1;
}
}
}
}
}
I tried to move the commented code to a separate function and needed to pass &mut t_contained
and &mut q_contained
but the compiler complained:
error[E0499]: cannot borrow `q_contained` as mutable more than once at a time
--> src\setup\container.rs:177:17
|
172 | } else if let Ok(mut t_contained) = q_contained.get_mut(removed_dragging_entity) {
| -------------------------------------------- first mutable borrow occurs here
173 | util_remove_from_container(
| -------------------------- first borrow later used by call
...
177 | &mut q_contained,
| ^^^^^^^^^^^^^^^^ second mutable borrow occurs here
For more information about this error, try `rustc --explain E0499`
I don't understand why the previous version of the code(the one without the function) worked, while the new one does not. I know that get_mut
takes &mut self
as an argument but still, I don't see why q_contained.get_mut(removed_dragging_entity)
counts as a borrow inside the if let statement.
Can this be fixed somehow, and are there better ways to write modular code and not pass every component separately or in a tuple?