Cannot borrow `*self` as mutable more than once at a time - how would idomatic Rust code looks like?


#21

Is making Delegate::execute borrow self immutably acceptable? That means any delegate that wants to mutate itself in that method would need to use interior mutability. The Queue in the example is simple to adapt - it would use a Cell<usize to mutate the index. I don’t know about other delegate impls that you have in mind.


#22

I’ve implementet it now with a VecDeque and left the part where you can loop the execution. But I’m still a bit unhappy, because I had to Box that much. Could you look over my Code and tell me, where I can do better?

use std::collections::VecDeque;

trait Bundle {}

trait Delegate<B: Bundle> {
    fn execute<'a>(&mut self, bundle: &'a mut B) -> &'a mut B;
}

trait Process<B: Bundle> {
    fn execute<'a>(&self, bundle: &'a mut B, next: &mut Delegate<B>) -> &'a mut B;
}

struct TB;

impl Bundle for TB {}

struct X;

struct Y;

impl Process<TB> for X {
    fn execute<'a>(&self, bundle: &'a mut TB, next: &mut Delegate<TB>) -> &'a mut TB {
        println!("I'm X");

        next.execute(bundle)
    }
}

impl Process<TB> for Y {
    fn execute<'a>(&self, bundle: &'a mut TB, next: &mut Delegate<TB>) -> &'a mut TB {
        println!("I'm Y");

        next.execute(bundle)
    }
}

struct Processor<B: Bundle> {
    queue: VecDeque<Box<Process<B>>>
}

impl<B: Bundle> Processor<B> {
    fn new(queue: VecDeque<Box<Process<B>>>) -> Self {
        Processor { queue }
    }
}

impl<B: Bundle> Delegate<B> for Processor<B> {
    fn execute<'a>(&mut self, bundle: &'a mut B) -> &'a mut B {
        if let Some(p) = self.queue.pop_back() {
            return p.execute(bundle, self);
        }

        bundle
    }
}

fn main() {
    let mut q: VecDeque<Box<Process<TB>>> = VecDeque::new();
    q.push_front(Box::new(X));
    q.push_front(Box::new(Y));
    let mut b = TB;
    let mut p = Processor::new(q);
    p.execute(&mut b);
}

#23

I don’t see excessive boxing given you’re working with trait objects. What part(s) bother you?


#24

Those are the parts which bothers me quite a bit:

The first two are so long, it reminds me of C++.

And I’m unsure if my usage of lifetimes for TB is correct.


#25

Box and lifetimes look right, (hard to say otherwise without knowing design intent.) Like you comment you are no longer looping. The code looks a bit odd (hard to read) with two executes but does not break.

I personally try and avoid specifying data types after variables. To do the same you can write instead;

let mut q = VecDeque::new();
q.push_front(Box::new(X) as Box<Process<_>>);

#26

Oh, that’s a nice trick.


#27

The following works just as well (all other code the same):

impl<B: Bundle> Processor<B> {
    fn new() -> Self {
        Self { queue: VecDeque::new() }
    }
    
    fn push_front<P: Process<B> + 'static>(&mut self, process: P) {
        self.queue.push_front(Box::new(process));        
    }
}

fn main() {
    let mut p = Processor::new();
    p.push_front(X);
    p.push_front(Y);
    let mut b = TB;
    p.execute(&mut b);
}

Also, if you run into long type names that you write over and over, consider using type aliases.


#28

What does that mean?


#29

It means push_front accepts any type that implements Process<B> and does not have any references internally (besides 'static). Box<SomeTrait> is actually Box<SomeTrait + 'static> - that’s the default object bound. So we’re just conveying the same information in push_front signature so that we can box up the value. internally.


#30

Thank you for the clarification. :slight_smile: