0

I am making a bot. When the bot receives a message it needs to check all the commands if they trigger on the message and if yes - perform an action.

So I have a vector of commands (Command trait) in main struct:

struct Bot {
    cmds: Vec<Box<Command>>,
}

Everything is good until I try to make a list of triggered commands and to use them later in (&self mut) method:

let mut triggered: Vec<Box<command::Command>>;
for c in &self.cmds {
    if c.check(&message) {
        triggered.push(c.clone());
    }
}

Error:

bot.rs:167:44: 167:56 error: mismatched types:
 expected `Box<Command>`,
    found `&Box<Command>`
(expected box,
    found &-ptr) [E0308]

What am I doing wrong here? I tried a lot but nothing helps. Initially I was doing the following:

for c in &self.cmds {
    if c.check(&message) {
        c.fire(&message, self);
    }
}

but it gave me:

bot.rs:172:46: 172:50 error: cannot borrow `*self` as mutable because `self.cmds` is also borrowed as immutable [E0502]
bot.rs:172        

                 c.fire(&message, self);

So I stackoverflowed it and came to solution above.

Community
  • 1
  • 1
VP.
  • 15,509
  • 17
  • 91
  • 161
  • Please provide a [mcve] that allows anyone else to reproduce the problem you are having. Otherwise we have to make so many guesses to build the code we could produce the wrong thing. – Shepmaster May 29 '16 at 13:18
  • Firstly, you should probably not be making a Vec>, since a vector will allocate all of its elements on the heap, so box-ing them again will just slow down element creation. – Lily Mara May 30 '16 at 01:42

1 Answers1

2

What am I doing wrong here? I tried a lot but nothing helps. Initially I was doing the following:

for c in &self.cmds {
    if c.check(&message) {
        c.fire(&message, self);
    }
}

If the fire function does not need access to other commands, an option is to temporarily replace self.cmd with an empty vector:

trait Command {
    fn check(&self, message: &str) -> bool;
    fn fire(&mut self, bot: &Bot);
}

struct Bot {
    cmds: Vec<Box<Command>>,
}

impl Bot {
    fn test(&mut self, message: &str) {
        use std::mem;
        // replace self.cmds with a empty vector and returns the
        // replaced vector
        let mut cmds = mem::replace(&mut self.cmds, Vec::default());
        for c in &mut cmds {
            if c.check(message) {
                c.fire(self);
            }
        }
        // put back cmds in self.cmds
        mem::replace(&mut self.cmds, cmds);
    }
}

There are other answers that use this approach.


If fire does need access to some fields of Bot, you can pass only the needed fields instead of self:

c.fire(self.others)
Community
  • 1
  • 1
malbarbo
  • 10,717
  • 1
  • 42
  • 57
  • OP states that `Command` is a trait, not a struct. This is why I want an MCVE... However, your second half is probably the right solution. You should explain why it's invalid to pass `self` when iterating over `self.messages` - how `fire` might try to modify `messages`. Alternatively, OP could break up `self` and only pass a subset of fields to `fire`. – Shepmaster May 29 '16 at 13:32
  • @Shepmaster Thanks to remind me that `Command` is a trait. I'm getting used to see when a question does not provide a MCVE... If `fire` receives `&Bot`, the case "how `fire` might try to modify messages" cannot happen and I think that the compiler is being rigid here. – malbarbo May 29 '16 at 13:51