How to move a `dyn T` object?

Hi,

For a state machine I thought this makes sense:

#![allow(unused)]
#![allow(dead_code)]

use std::fmt::Debug;

type TransistionResult = std::result::Result<Box<dyn State>, Box<dyn std::error::Error>>;
type StateBox = Box<dyn State>;

trait State: Debug + Send + Sync {
    fn feed(&self, chars: &[u8]) -> TransistionResult;
}

#[derive(Debug)]
struct Hello {
    
}
 
impl State for Hello {
    fn feed(&self, chars: &[u8]) -> TransistionResult {
        println!("Transitioning ...");
        Ok(Box::new(Bye {}))
    }
}

#[derive(Debug)]
struct Bye {}
 
impl State for Bye {
    fn feed(&self, chars: &[u8]) -> TransistionResult {
        println!("Staying in the same state.");
        Ok(Box::new(Bye {}))
    }
}

fn main() {
    let mut state: StateBox = Box::new(Hello {});
    let foo = [1u8,2,3];
    for _ in 0..2 {
        state = state.feed(&foo).unwrap();
    }
    
    dbg!(state);
}

Permalink to the playground

It seem to work. However, when in the Bye state I want to simply return the same Box again, I struggle:

I made the methods use self in contrast to &self which results in a move and returned a Box::new(self).

impl State for Bye {
    fn feed(self, chars: &[u8]) -> TransistionResult {
        println!("Staying in the same state.");
        Ok(Box::new(self))
    }
}

This does not work:

error[E0161]: cannot move a value of type `dyn State`
  --> src/main.rs:39:17
   |
39 |         state = state.feed(&foo).unwrap();
   |                 ^^^^^ the size of `dyn State` cannot be statically determined

Permalink to the playground

What would be a better solution here?

Thanks,
Philipp

As you rightfully deduced, trait methods that take self as an argument are not object safely dispatchable, due to the Sized requirement on Self. dyn Trait being dynamically sized, so it doesn't satisfy the Sized bound. However, you can pass self not just by reference &self or &mut self or by value self, but also by smart pointers, including Box<Self>:

#![allow(unused)]
#![allow(dead_code)]

use std::fmt::Debug;

type TransistionResult = std::result::Result<Box<dyn State>, Box<dyn std::error::Error>>;
type StateBox = Box<dyn State>;

trait State: Debug + Send + Sync {
    fn feed(self: Box<Self>, chars: &[u8]) -> TransistionResult;
}

#[derive(Debug)]
struct Hello {
    
}
 
impl State for Hello {
    fn feed(self: Box<Self>, chars: &[u8]) -> TransistionResult {
        println!("Transitioning ...");
        Ok(Box::new(Bye {}))
    }
}

#[derive(Debug)]
struct Bye {}
 
impl State for Bye {
    fn feed(self: Box<Self>, chars: &[u8]) -> TransistionResult {
        println!("Staying in the same state.");
        Ok(self)
    }
}

fn main() {
    let mut state: StateBox = Box::new(Hello {});
    let foo = [1u8,2,3];
    for _ in 0..2 {
        state = state.feed(&foo).unwrap();
    }
    
    dbg!(state);
}

Playground.

4 Likes

Additionally, you can also impl<T: State> State for Box<T>.

1 Like

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.