Struggling with borrow checker - mutable borrow of Vec


#1

Hello! I’m trying to make a simple game in Rust, and I’m struggling with the borrow checker…

1. The full code:


http://pastebin.com/7c9YNbJ2

2. The part that is giving me trouble:


impl<'a> Game<'a> {
    fn check(&mut self, position: &structs::Position, direction: Direction) {
        use Direction::*;

        let mut next = (*position).clone();
        let mut is_crt = false;
        let mut obj = None;

        match direction {
            Down => next = next + structs::Position::new(0, 1).unwrap(),
            Up => next = next + structs::Position::new(0, -1).unwrap(),
            Left => next = next + structs::Position::new(-1, 0).unwrap(),
            Right => next = next + structs::Position::new(1, 0).unwrap(),
        }
        
        {
            let mut iter = self.special.iter_mut();
            let fnd = iter
                .find(|&&mut ref x| x.position == next && !(x.obj_type == ObjectType::Target));
            if fnd.is_none() {
                println!("did not find obstacle for player at: {:?}", next);
                self.player.position = next;
            } else {
                match direction {
                    Down => next = next + structs::Position::new(0, 1).unwrap(),
                    Up => next = next + structs::Position::new(0, -1).unwrap(),
                    Left => next = next + structs::Position::new(-1, 0).unwrap(),
                    Right => next = next + structs::Position::new(1, 0).unwrap(),
                }

                obj = fnd;

                if obj.as_ref().unwrap().obj_type == ObjectType::Crate {
                    is_crt = true;
                }
            }

        }
        {
            if is_crt {
                let mut iter = self.special.iter_mut();
                let fnd = iter.find(|&&mut ref x| x.position == next);
                if fnd.is_none() {
                    println!("did not find crate obstacle at: {:?}", next);
                    obj.unwrap().position = next;
                } else {
                    let mut obj2 = fnd.unwrap();
                    if obj2.obj_type == ObjectType::Target {
                       obj2.position = structs::Position::new(0, 0).unwrap();
                    }
                }
            }
        }

    }
//...
}

3. The error


error[E0499]: cannot borrow `self.special` as mutable more than once at a time
   --> src/main.rs:100:32
    |
76  |             let mut iter = self.special.iter_mut();
    |                            ------------ first mutable borrow occurs here
...
100 |                 let mut iter = self.special.iter_mut();
    |                                ^^^^^^^^^^^^ second mutable borrow occurs here
...
114 |     }
    |     - first borrow ends here

4. What I tried to do:


Basically what I needed to do was to search if the player’s next position was some obstacle, and a possible movable crate.
First iterator: search for player obstacles
Second Iterator: search for crate obstacles (and possible target)
Though Rust borrow checker didn’t let me do this. So I tried using scopes. But it did not work either (probably because I stored the obstacle in the obj variable. Using Iterator.by_ref() to use the same Iterator did not work either by the way.
I need some help… Can’t really figure out how to solve this, been stuck for hours.
Any question I’ll be glad to answer!
Thanks!


#2

Just curious, why do you need an iter_mut? I don’t see where you are mutating the elements of special.


#3

Rust can’t prove that the objects found in both iterators are not the same objects. You just call find on the iterator. Rust doesn’t analyse the condition inside the find's closure at all, so in Rust typesystem, it’s entirely possible that both iterators will return the same object – and that would violate the rule of “no two &muts pointing at the same thing”.

The simplest workaround would be for you to just store the index, instead of the &mut reference, at least for the first iterator. Rust iterators come with a handy enumerate() method that make this easy.

Also, some minor nits about your code :slight_smile:

|&&mut ref x| x.position == next – the dot operator in Rust auto-dereferences, so it would be enough to just write |x|.

Also, it’s an antipattern to do things like

if opt.is_some() { ... opt.unwrap() ... } ...

Better to use if-let (or match)

if let Some(thing) = opt { ... thing ... } ...

#4

I need to change the position of the object (crate).
It’s not great how it is right now because i was experimenting but basically i need to do something like
obj.position = next;
obj being the crate found, and next being the next position.


#5

Thanks, I’ll try that, seems to be a great alternative! And yeah i totally forgot about if let and the &&mut ref x was just overkill, though I was struggling since if i’m not wrong I was getting ‘moved’ error.