0

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?

marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
OOrosh
  • 1

0 Answers0