[back]
Rust (Challenge) Port Simple State Machine
modified: 2015-03-22 04:22:15

A friend suggested porting the following code from Nim to Rust. I have never used Nim but could grasp the intention of the code. I had to make a choice along the way. Was I going to support multiple Worker being able to use the same State, and was I going to support multiple State being able to use the same worker. Well, I decide to allow multiple workers to use the same state and left it open to be implemented for state to be able to implement a field to hold a specific worker.

The following is the Nim code:

    type
    State = object of RootObj
    worker: ref Worker
    
    Worker = object
    state: ref State
    
    method enter(self: ref State) =
    echo("Default State.enter()")
    
    method work(self: ref State) =
    echo("Default State.work()")
    
    method leave(self: ref State) =
    echo("Default State.leave()")
    
    proc set_state(self: ref Worker, state: ref State) =
    self.state.leave()
    self.state = state
    self.state.worker = self
    self.state.enter()
    
    proc work(self: ref Worker) =
    echo("Doing work")
    self.state.work()
    
    type
    AState = object of State
    BState = object of State
    
    method work(self: ref AState) =
    echo("Working from AState")
    self.worker.set_state(new(BState))
    
    method work(self: ref BState) =
    echo("Working from BState")
    
    
    var worker = new(Worker)
    var state = new(AState)
    worker.set_state(state)
    worker.work()
    worker.work()

The following is the Rust code:

    use std::rc::Rc;
    use std::cell::RefCell;

    trait Worker<'a> {
        fn set_state(&mut self, state: Rc<RefCell<Box<State + 'a>>>);
        fn work(&mut self);
    }

    struct WorkerA<'a> {
        state:      Option<Rc<RefCell<Box<State + 'a>>>>,
    }

    impl<'a> WorkerA<'a> {
        fn new() -> WorkerA<'a> {
            WorkerA { state: None }
        }
    }

    impl<'a> Worker<'a> for WorkerA<'a> {
        fn set_state(&mut self, state: Rc<RefCell<Box<State + 'a>>>) {
            self.state = Some(state);
        }
    
        fn work(&mut self) {
            if self.state.is_none() {
                return;
            }

            let unsafecopy: &mut WorkerA;
    
            /*
                The problem is we will grab a mutable reference to the
                inner field of self below. With a mutable reference we
                have locked self. So we can not pass self to the method
                and we need to. So we have to unsafely duplicate the
                pointer. There will be two mutable pointers that can
                point to the same data once this is completed!
            */
            unsafe {
                unsafecopy = std::mem::transmute_copy(&self);
            }
    
            (**self.state.as_mut().unwrap()).borrow_mut().work(unsafecopy);
    
            //let state: &mut State = &mut ***self.state.as_mut().unwrap();
            //state.work(unsafecopy);
        }
    }

    trait State {
        fn work(&self, worker: &mut Worker);
    }

    struct StateA {
        a:      uint
    }

    impl State for StateA {
        fn work(&self, worker: &mut Worker) {
            println!("working from a-state");
    
            let x: Box<State> = box StateB { a: 0 };
            let y: Rc<RefCell<Box<State>>> = Rc::new(RefCell::new(x));
    
            worker.set_state(y);
        }
    }

    struct StateB {
        a:      uint
    }

    impl State for StateB {
        fn work(&self, worker: &mut Worker) {
            println!("working from b-state");
        }
    }

    fn main() {
        let mut worker: WorkerA = WorkerA::new();
        let statea: Box<StateA> = box StateA { a: 0 };

        let x: Box<State> = statea;
        let y: Rc<RefCell<Box<State>>> = Rc::new(RefCell::new(x));
    
        worker.set_state(y);
    
        worker.work();
        worker.work();
    }