Using trait objects

To learn rust I start to implement tetris game.
I tried to implement state pattern in rust. Unfortunately it's not as easy as in c++
My first implementation was

pub trait State {
    fn update(&mut self,  data : &mut Tetris, update_args : &UpdateArgs) -> Rc<dyn State>;
    fn render(&mut self,  data : &mut Tetris, c : Context,  g : &mut G2d);
}

Where Tetris contain Vec of these Rc but Rc is read only and use of refCell do not feel right. Also I do not know how to Box<something...> without erase the value from the vec that should contain all the instances

I would generally not recommend using a trait for this. Just implement the methods directly on a struct.

At some point I should learn how to use polymorphism in rust and the whole idea behind this project is to learn rust

Can you give some more details about the specific problem at hand? Then I can give some suggestions on how best to do it, if you must use traits to support many implementations of State.

In particular, I want some details regarding that Rc. Do you really need the data to have shared ownership?

I have object named game_logic which have array of trait objects State and trait object current which also is State. Current should be just a reference to one of the object from the array, index in the array is also variant. State implement update method which should return new current state and should be called on current state. As I already mentioned I tried to implement that with Rc but Rc are read only. I can bypass that with refCell it fill crappy to me. Do you have idea how to do that in good way ?

If you wish to avoid RefCell, you must avoid shared ownership.

Since you advance the state by creating a new one, I would go for the following. The functions take immutable references, as the state is mutated by creating a new state.

pub trait State {
    fn update(&self, data: &mut Tetris, update_args: &UpdateArgs) -> Box<dyn State>;
    fn render(&self, data: &mut Tetris, c: Context, g: &mut G2d);
}

I do not want shared state and I do not want to recreate states. I want single struct to contain all the States and some kind of reference to one of the states

Rust is very strict about things being either shared-immutable or exclusive-mutable. Rc is shared, so it can't hold anything directly mutable.

Rc<RefCell> is shareable and can be mutated, because RefCell ensures both can't happen at the same time.

You could return Box<dyn State> to have it exclusive-mutable. If you're just learning, start with code that works, and only later make it efficient.

When you choose a design that cannot be proven statically, RefCell is the right thing. Or change the design to avoid shared (Rc) mutable (&mut self) state.

If I understand correctly, you want to store different states in a Vec without erasing types of the states by using Box<dyn State>. You can do this by storing states in an enum

enum States {
   State1(State1Struct),
   State2(State2Struct),
}
1 Like

More likely I do not want to erase the instance. Don't want to have unnecessary allocation and
deallocation

You are welcome to attempt any design pattern you like, but my recommendation is that if you want to learn rust that you be open to design patterns that work well in rust.

Polymorphism doesn't work well in rust, or in C++, for that matter. But in C++ it's often your only tool, so every problem looks like a polymorphism problem. Rust gives you more powerful tools (in this context enum), and it's worth learning to use them, even if it's unfamiliar.

I'm sure it's true that there are some problems that dyn Trait can solve, but they are relatively rare, so rare that I haven't encountered one. The only situation that comes to mind is if you have code that needs to deal with a collection of objects of types that are defined in different crates you don't control.

1 Like

Returning of dyn Trait requires allocation, because the concrete types that implement the trait may have different size in memory, and the function calling convention is incapable of returning a number of bytes that the caller doesn't know. There has to be a space in the stack frame reserved for the object, but if the type is dynamic, the caller can't possibly know how much stack to reserve for the returned value. Returning by reference (Box or Rc) makes all types have the same fixed size — size of two pointers for data & vtable.

I presume that in C++ you'd use new or something like unique_ptr, which use the heap. If you've tried to return subclasses by value, you'd run into C++ object slicing footgun.

If you post your C++ solution we can help you translate it to equivalent Rust.

1 Like

sorry for late answer. I just figure it out. I can use boxes and return references from them using lifetimes.

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.