How can I deal with the `borrowing` issue

When I wrote the following code, some confusing compile errors occurred. Can anyone tell me how to deal with it?

Code:

Stage::Graduated(vision) => {
    if vision.show_panel {
        let mut tree = vec![];
        for _ in vision.images.iter() {
            tree.push(iced_native::widget::tree::Tree::empty());
        }
        let mut current_tree;
        let mut points = iced_native::overlay::Group::new();
        for (index, _i) in vision.images.iter().enumerate() {
            current_tree = &mut tree[index];
            let pinpoint = crate::button_from_svg();
            let floating_element = FloatingElementOverlay::new(
                &mut current_tree,
                pinpoint,
                &Anchor::SouthEast,
                &Offset { x: 0.0, y: 0.0 },
            );
            points = points.push(floating_element.overlay(Point { x: 0.0, y: 0.0 }));
        }
        return column![points].into();
    } else {
        return column![].into();
    }
}

Errors:

PS D:\graduate> cargo run
   Compiling graduate v0.1.0 (C:\Users\amazi\graduate)
error[E0506]: cannot assign to `current_tree` because it is borrowed
    --> src\main.rs:1172:33
     |
1172 | ... current_tree = &mut tree[index];
     |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `current_tree` is assigned to here but it was already borrowed
...
1180 | ... &mut current_tree,
     |     ----------------- `current_tree` is borrowed here
...
1185 | ... points = points.push(overlay.overlay(Point { x: 0.0, y: 0.0 }));
     |              ------ borrow later used here

error[E0499]: cannot borrow `tree` as mutable more than once at a time
    --> src\main.rs:1172:53
     |
1172 |     current_tree = &mut tree[index];
     |                                 ^^^^ `tree` was mutably borrowed here in the previous iteration of the loop
...
1206 |     } else {
     |     - first borrow might be used here, when `points` is dropped and runs the destructor for type `Group<'_, Message, iced_graphics::renderer::Renderer<iced_wgpu::backend::Backend, Theme>>`

Some errors have detailed explanations: E0499, E0506.
For more information about an error, try `rustc --explain E0499`.
error: could not compile `graduate` due to 2 previous errors

I believe that the issue is current_tree = &mut tree[index] borrowing all of tree every loop iteration. This wouldn't normally be a problem, but in this case you're storing the result in points which lives beyond the end of the loop. So current_tree from the second loop iteration exists at the same time as current_tree from the first, which is disallowed.

To make this work, you need to split a single borrow of tree into separate borrows of the individual elements, which you can do with tree.iter_mut():

(untested)

        for (current_tree, _i) in std::iter::zip(tree.iter_mut(), vision.images.iter()) {
            // ...
        }

It fixes! Thank you very much!

That isn't enough, because returning points would be a UAF when tree is dropped.

I don't actually know how the FloatingElementOverlay struct is intended to be used. But the docs indicate that this is an internal implementation detail. The example uses FloatingElement.

2 Likes

When I then tried to use your code in a similar way, another error appeared. I wonder why it violated Rust's borrow rule while the previous snippet didn't.

I would really appreciate it if you can help me understand them as well.

Code:

fn overlay<'b>(
    &'b mut self,
    state: &'b mut Tree,
    layout: Layout<'_>,
    renderer: &Renderer,
) -> Option<overlay::Element<'b, Message, Renderer>> {
    let bounds = layout.bounds();
    let position = Point::new(0.0, 0.0);
    let res = (self.element)();
    let mut group = iced_native::overlay::Group::new();
    let mut empty_state = vec![];
    for _ in res.iter() {
        empty_state.push(iced_native::widget::tree::Tree::empty());
    }
    for (i, cur) in std::iter::zip(res.into_iter(), empty_state.iter_mut()) {
        group = group.push(
            FloatingElementOverlay::new(cur, i, &self.anchor, &self.offset)
                .overlay(position),
        );
    }
    Some(group.into())
}

The Error:

error[E0515]: cannot return value referencing local variable `empty_state`
   --> src\floatingelement.rs:222:13
    |
216 | for (i, cur) in std::iter::zip(res.into_iter(), empty_state.iter_mut()) {
    |                                                 ---------------------- `empty_state` is borrowed here
...
222 | Some(group.into())
    | ^^^^^^^^^^^^^^^^^^ returns a value referencing data owned by the current function

I realized what the problem was. Like the compiler says, I should make empty_state the widget's accessory variable.

The error message is telling you exactly what I commented on. You can't return anything that references a temporary.

To solve the problem, look at the example link I provided. That's the supported way of using floating elements.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.