Borrowing GameMaster (self) to GameState - rust

I'm fairly new to Rust and I've been trying to get a simple game working with ggez.
Now, normally in C++ or C# I would structure my game around having an instance of a "master" class that takes care of settings, frame limiting, window creation, event handling, etc., and "state" classes that are held by the master class. I would pass a pointer to the master class when creating the state class (something like game_master*) so that the state class can access the resources of the master class.
In Rust I can't pass a &mut self because the state could potentially outlive the reference.
pub fn new(_ctx: &mut Context) -> GameMaster {
let game = GameMaster {
state: None,
pending_state: None
};
game.state = Some(Box::new(TestState::new_with_master(&mut game))) <----- NOPE
game
}
I think this could be solved with lifetimes but I haven't found a way to get lifetimes working with traits. Passing the master in as a reference on every function call doesn't work either because only one mutable reference can be held at a time.
fn update(&mut self, ctx: &mut Context) -> GameResult<()> {
if self.state.is_some() {
(*self.state.as_mut().unwrap()).update(ctx, &mut self) <----- NOPE
}
else {
Ok(())
}
}
Is there a good way to do this in Rust?

I have a similar problem a while ago but I think the only choices are:
use Rc<RefCell<>> everywhere. Remember to also use Weak or add a special destroy method if you ever need to garbage collect these objects since they hold reference to each other.
use raw pointers and unsafe code. The problem is that with unsafe deference, you also lose the mutability check (RefCell ensures only one mutable reference exists at a time) in addition to the lifetime check.

Related

Rust mutable container of immutable elements?

With Rust, is it in general possible to have a mutable container of immutable values?
Example:
struct TestStruct { value: i32 }
fn test_fn()
{
let immutable_instance = TestStruct{value: 123};
let immutable_box = Box::new(immutable_instance);
let mut mutable_vector = vec!(immutable_box);
mutable_vector[0].value = 456;
}
Here, my TestStruct instance is wrapped in two containers: a Box, then a Vec. From the perspective of a new Rust user it's surprising that moving the Box into the Vec makes both the Box and the TestStruct instance mutable.
Is there a similar construct whereby the boxed value is immutable, but the container of boxes is mutable? More generally, is it possible to have multiple "layers" of containers without the whole tree being either mutable or immutable?
Is there a similar construct whereby the boxed value is immutable, but the container of boxes is mutable? More generally, is it possible to have multiple "layers" of containers without the whole tree being either mutable or immutable?
Not really. You could easily create one (just create a wrapper object which implements Deref but not DerefMut), but the reality is that Rust doesn't really see (im)mutability that way, because its main concern is controlling sharing / visibility.
After all, for an external observer what difference is there between
mutable_vector[0].value = 456;
and
mutable_vector[0] = Box::new(TestStruct{value: 456});
?
None is the answer, because Rust's ownership system means it's not possible for an observer to have kept a handle on the original TestStruct, thus they can't know whether that structure was replaced or modified in place[1][2].
If you want to secure your internal state, use visibility instead: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8a9346072b32cedcf2fccc0eeb9f55c5
mod foo {
pub struct TestStruct { value: i32 }
impl TestStruct {
pub fn new(value: i32) -> Self { Self { value } }
}
}
fn test_fn() {
let immutable_instance = foo::TestStruct{value: 123};
let immutable_box = Box::new(immutable_instance);
let mut mutable_vector = vec!(immutable_box);
mutable_vector[0].value = 456;
}
does not compile because from the point of view of test_fn, TestStruct::value is not accessible. Therefore test_fn has no way to mutate a TestStruct unless you add an &mut method on it.
[1]: technically they could check the address in memory and that might tell them, but even then it's not a sure thing (in either direction) hence pinning being a thing.
[2]: this observability distinction is also embraced by other languages, for instance the Clojure language largely falls on the "immutable all the things" side, however it has a concept of transients which allow locally mutable objects

Having trouble with proper design pattern

I'm trying to write a simple game engine, but the normal patterns I would use to do this don't work here due to the strict borrowing rules.
There is a World struct which owns a collection of Object trait objects. We can think of these as moving physical objects in the game engine. World is responsible for calling update and draw on each of these during each game tick.
The World struct also owns a collection of Event trait objects. Each of these events encapsulates an arbitrary piece of code that modifies the objects during the game ticks.
Ideally, the Event trait object has a single method do_event that takes no arguments that can be called by World during each game tick. The problem is that a particular Event needs to have mutable references to the objects that it modifies but these objects are owned by World. We could have World pass mutable references to the necessary objects when it calls do_event for each event, but how does it know which objects to pass to each event object?
If this were C++ or Python, when constructing a particular Event, I would pass the target of the event to the constructor where it would be saved and then used during do_event. Obviously, this doesn't work in Rust because the targets are all owned by World so we cannot store mutable references elsewhere.
I could add some ID system and a lookup table so World knows which objects to pass to which Events but this is messy and costly. I suspect my entire way of approaching this class of problem needs to change, however, I am stumped.
A sketch of my code:
trait Object {
fn update(&mut self);
fn draw(&self);
}
trait Event {
fn do_event(&mut self);
}
struct World {
objects: Vec<Box<dyn Object + 'a>>,
events: Vec<Box<dyn Event + 'a>>,
}
impl World {
fn update(&mut self) {
for obj in self.objects.iter_mut() {
obj.update();
}
for evt in self.events.iter_mut() {
evt.do_event();
}
}
fn draw(&mut self) { /*...*/ }
}
struct SomeEvent<'a, T: Object> {
target: &'a mut T,
}
impl<'a, T: Object> Event for SomeEvent<'a, T> {
fn update(&self) {
self.target.do_shrink(); // need mutable reference here
}
}
I know that RefCell enables multiple objects to obtain mutable references. Perhaps that is the direction I should go. However, based on what I learned from the Rust book, I got the sense that RefCells break some of Rust's main ideas by introducing unsafe code and circular references. I guess I was just wondering if there was some other obvious design pattern that adhered more to the idiomatic way of doing things.
After reading the question comments and watching kyren's RustConf 2018 talk (thanks trentcl), I've realized the OO approach to my game engine is fundamentally incompatible with Rust (or at least quite difficult).
After working on this for a day or so, I would highly recommend watching the posted video and then using the specs library (an implementation of the ECS system mentioned by user2722968).
Hope this helps someone else in the future.

Create two mutable references that are thread safe to a struct in Rust

I'm trying to create async Reader and Writer in tokio, these require Send, and must be thread safe. (does not seem to be a way to write single threaded tokio code that avoids mutexts)
The reader and writer both need to interact, e.g read data might result in a response.
I would like both Reader and Writer to have a thread safe pointer to a Session that can ensure communication between the two.
/// function on the
impl Session {
pub fn split(&mut self, sock: TcpStream) -> (Reader, Writer) {
let (read_half, write_half) = sock.split();
let session = Arc::new(RwLock::new(self)); // <- expected lifetime
( Reader::new(session, read_half), Writer::new(Arc::clone(session), write_half) )
}
...
}
pub struct Reader {
session: Arc<RwLock<&mut StompSession>>,
read_half: ReadHalf<TcpStream>,
...
pub struct Writer {
session: Arc<RwLock<&mut StompSession>>,
write_half: WriteHalf<TcpStream>,
....
I dont really understand the difference between Arc<RwLock<&mut StompSession>> and Arc<RwLock<StompSession>> both can only be talking about pointers.
Naturally come at this by stuggling with the borrow checker and rust book only has examples for RRwLock with integers, not mutable "objects".
Let's start by clearing a few things:
does not seem to be a way to write single threaded tokio code that avoids mutexes
The Mutex requirement has nothing to do with single-threadedness but with mutable borrows. Whenever you spawn a future, that future is its own entity; it isn't magically part of your struct and most definitely does not know how to keep a &mut self. That is the point of the Mutex - it allows you to dynamically acquire a mutable reference to inner state - and the Arc allows you to have access to the Mutex itself in multiple places.
Their non-synchronized equivalents are Rc and Cell/RefCell, by the way, and their content (whether synchronized or unsynchronized) should be an owned type.
The Send requirement actually shows up when you use futures on top of tokio, as an Executor requires futures spawned on it to be Send (for obvious reasons - you could make a spawn_local method, but that'd cause more problems than it solves).
Now, back to your problem. I'm going to give you the shortest path to an answer to your problem. This, however, will not be completely the right way of doing things; however, since I do not know what protocol you're going to lay on top of TcpStream or what kind of requirements you have, I cannot point you in the right direction (yet). Comments are there for that reason - give me more requirements and I'll happily edit this!
Anyway. Back to the problem. Since Mutex<_> is better used with an owned type, we're going to do that right now and "fix" both your Reader and Writer:
pub struct Reader {
session: Arc<RwLock<Session>>,
read_half: ReadHalf<TcpStream>,
}
pub struct Writer {
session: Arc<RwLock<StompSession>>,
write_half: WriteHalf<TcpStream>,
}
Since we changed this, we also need to reflect it elsewhere, but to be able to do this we're going to need to consume self. It is fine, however, as we're going to have an Arc<Mutex<_>> copy in either object we return:
impl Session {
pub fn split(self, sock: TcpStream) -> (Reader, Writer) {
let (read_half, write_half) = sock.split();
let session = Arc::new(RwLock::new(self));
( Reader::new(session.clone(), read_half), Writer::new(session, write_half) )
}
}
And lo and behold, it compiles and each Writer/Reader pair now has its own borrowable (mutably and non-mutably) reference to our session!
The playground snippet highlights the changes made. As I said, it works now, but it's going to bite you in the ass the moment you try to do something as you're going to need something on top of both ReadHalf and WriteHalf to be able to use them properly.

Getting around Rust ownership problems when using state machine pattern

This question is about a specific pattern of ownership that may arise when implementing a state machine for a video game in Rust, where states can hold a reference to "global" borrowed context and where state machines own their states. I've tried to cut out as many details as I can while still motivating the problem, but it's a fairly large and tangled issue.
Here is the state trait:
pub trait AppState<'a> {
fn update(&mut self, Duration) -> Option<Box<AppState<'a> + 'a>>;
fn enter(&mut self, Box<AppState<'a> + 'a>);
//a number of other methods
}
I'm implementing states with a boxed trait object instead of an enum because I expect to have quite a lot of them. States return a Some(State) in their update method in order to cause their owning state machine to switch to a new state. I added a lifetime parameter because without it, the compiler was generating boxes with type: Box<AppState + 'static>, making the boxes useless because states contain mutable state.
Speaking of state machines, here it is:
pub struct StateMachine<'s> {
current_state: Box<AppState<'s> + 's>,
}
impl<'s> StateMachine<'s> {
pub fn switch_state(&'s mut self, new_state: Box<AppState<'s> + 's>) -> Box<AppState<'s> + 's> {
mem::replace(&mut self.current_state, new_state);
}
}
A state machine always has a valid state. By default, it starts with a Box<NullState>, which is a state that does nothing. I have omitted NullState for brevity. By itself, this seems to compile fine.
The InGame state is designed to implement a basic gameplay scenario:
type TexCreator = TextureCreator<WindowContext>;
pub struct InGame<'tc> {
app: AppControl,
tex_creator: &'tc TexCreator,
tileset: Tileset<'tc>,
}
impl<'tc> InGame<'tc> {
pub fn new(app: AppControl, tex_creator: &'tc TexCreator) -> InGame<'tc> {
// ... load tileset ...
InGame {
app,
tex_creator,
tileset,
}
}
}
This game depends on Rust SDL2. This particular set of bindings requires that textures be created by a TextureCreator, and that the textures not outlive their creator. Texture requires a lifetime parameter to ensure this. Tileset holds a texture and therefore exports this requirement. This means that I cannot store a TextureCreator within the state itself (though I'd like to), since a mutably-borrowed InGame could have texture creator moved out. Therefore, the texture creator is owned in main, where a reference to it is passed to when we create our main state:
fn main() {
let app_control = // ...
let tex_creator = // ...
let in_game = Box::new(states::InGame::new(app_control, &tex_creator));
let state_machine = states::StateMachine::new();
state_machine.switch_state(in_game);
}
I feel this program should be valid, because I have ensured that tex_creator outlives any possible state, and that state machine is the least long-lived variable. However, I get the following error:
error[E0597]: `state_machine` does not live long enough
--> src\main.rs:46:1
|
39 | state_machine.switch_state( in_game );
| ------------- borrow occurs here
...
46 | }
| ^ `state_machine` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
This doesn't make sense to me, because state_machine is only borrowed by the method invocation, but the compiler is saying that it's still borrowed when the method is over. I wish it let me trace who the borrower in the error message--I don't understand why the borrow isn't returned when the method returns.
Essentially, I want the following:
That states be implemented by trait.
That states be owned by the state machine.
That states be able to contain references to arbitrary non-static data with lifetime greater than that of the state machine.
That when a state is swapped out, the old box be still valid so that it can be moved into the constructor of the new state. This will allow the new state to switch back to the preceding state without requiring it to be re-constructed.
That a state can signal a state change by returning a new state from 'update'. The old state must be able to construct this new state within itself.
Are these constraints possible to satisfy, and if so, how?
I apologize for the long-winded question and the likelihood that I've missed something obvious, as there are a number of decisions made in the implementation above where I'm not confident I understand the semantics of the lifetimes. I've tried to search for examples of this pattern online, but it seems a lot more complicated and constrained than the toy examples I've seen.
In StateMachine::switch_state, you don't want to use the 's lifetime on &mut self; 's represents the lifetime of resources borrowed by a state, not the lifetime of the state machine. Notice that by doing that, the type of self ends up with 's twice: the full type is &'s mut StateMachine<'s>; you only need to use 's on StateMachine, not on the reference.
In a mutable reference (&'a mut T), T is invariant, hence 's is invariant too. This means that the compiler considers that the state machine has the same lifetime as whatever it borrows. Therefore, after calling switch_state, the compiler considers that the state machine ends up borrowing itself.
In short, change &'s mut self to &mut self:
impl<'s> StateMachine<'s> {
pub fn switch_state(&mut self, new_state: Box<AppState<'s> + 's>) -> Box<AppState<'s> + 's> {
mem::replace(&mut self.current_state, new_state)
}
}
You also need to declare state_machine in main as mutable:
let mut state_machine = states::StateMachine::new();

Borrow data out of a mutex "borrowed value does not live long enough"

How can I return an iterator over data within a mutex which itself is contained within a struct. The error the compiler gives is "borrowed value does not live long enough".
How do I get the lifetime of the value to extend into the outer scope?
Here is a minimal demo of what I am trying to achieve.
use std::sync::{Mutex, Arc};
use std::vec::{Vec};
use std::slice::{Iter};
#[derive(Debug)]
struct SharedVec {
pub data: Arc<Mutex<Vec<u32>>>,
}
impl SharedVec {
fn iter(& self) -> Iter<u32> {
self.data.lock().unwrap().iter()
}
}
fn main() {
let sv = SharedVec {
data: Arc::new(Mutex::new(vec![1, 2, 3, 4, 5]))
};
for element in sv.data.lock().unwrap().iter() { // This works
println!("{:?}", element);
}
for element in sv.iter() { // This does not work
println!("{:?}", element);
}
}
Rust playground link: http://is.gd/voukyN
You cannot do it exactly how you have written here.
Mutexes in Rust use RAII pattern for acquisition and freeing, that is, you acquire a mutex when you call the corresponding method on it which returns a special guard value. When this guard goes out of scope, the mutex is released.
To make this pattern safe Rust uses its borrowing system. You can access the value inside the mutex only through the guard returned by lock(), and you only can do so by reference - MutexGuard<T> implements Deref<Target=T> and DerefMut<Target=T>, so you can get &T or &mut T out of it.
This means that every value you derive from a mutexed value will necessarily have its lifetime linked to the lifetime of the guard. However, in your case you're trying to return Iter<u32> with its lifetime parameter tied to the lifetime of self. The following is the full signature of iter() method, without lifetime parameters elision, and its body with explicit temporary variables:
fn iter<'a>(&'a self) -> Iter<'a, u32> {
let guard = self.data.lock().unwrap();
guard.iter()
}
Here the lifetime of guard.iter() result is tied to the one guard, which is strictly smaller than 'a because guard only lives inside the scope of the method body. This is a violation of borrowing rules, and so the compiler fails with an error.
When iter() returns, guard is destroyed and the lock is released, so Rust in fact prevented you from making an actual logical error! The same code in C++ would compile and behave incorrectly because you would access protected data without locking it, causing data races at the very least. Just another demonstration of the power of Rust :)
I don't think you'll be able to do what you want without nasty hacks or boilerplate wrappers around standard types. And I personally think this is good - you have to manage your mutexes as explicit as possible in order to avoid deadlocks and other nasty concurrency problems. And Rust already makes your life much easier because it enforces absence of data races through its borrowing system, which is exactly the reason why the guard system behaves as described above.
As Vladimir Matveev's answer mentions, this isn't possible with return values. You can achieve your goal if you pass the iterator into a function instead of returning it:
impl SharedVec {
fn iter<R>(&self, func: impl FnOnce(Iter<'_, u32>) -> R) -> R {
let guard = self.data.lock().unwrap();
func(guard.iter())
}
}
This function is used like this:
sv.iter(|iter| {
for element in iter {
println!("{:?}", element);
}
});
This type of function wrapping will have to be repeated with every type of iterator. If you end up doing that, it may be easier to hand over a mutable slice or &mut SharedVec instead, making the closure choose the iteration method.
This method works because you never release the lock keeping the data protected from multiple threads from writing at the same time.

Resources