State machines with lib, expected lifetime parameter

I'm trying out this nice state machine library at https://github.com/rust-bakery/machine.

It complains about:
--> src/main.rs:27:15
|
27 | (IsTaken, DropItem) => IsDropped,
| ^^^^^^^^ expected lifetime parameter

because the struct DropItem is apparently not allowed to have lifetime references, maybe a limitation of the library. The mutable reference is called bang and I'd like to have it available on transition.

Using a second parameter for IsTaken::on_drop_item also errors out.

Is there a way to use mutable references in this case for messages/state change at all?
The last resort would be to kill the macros and write everything by hand.

My code: (playground does not compile because it's missing the lib called machine)

#[macro_use]
extern crate machine;

enum State {
	TakeItem,
	DropItem,
}

machine!(
  #[derive(Clone,Debug,PartialEq)]
  enum Weight {
    IsTaken { weight: usize },
    IsDropped,
  }
);

#[derive(Clone,Debug,PartialEq)]
//pub struct DropItem; //without reference
pub struct DropItem<'a> { //this does not work
    bang: &'a mut Bang,
}

#[derive(Clone,Debug,PartialEq)]
pub struct TakeItem;

transitions!(Weight,
  [
    (IsTaken, DropItem) => IsDropped,
    (IsDropped, DropItem) => IsDropped, 
    (IsDropped, TakeItem) => IsTaken,
    (IsTaken, TakeItem) => IsTaken
  ]
);

impl IsTaken {
	pub fn on_drop_item(self, message: DropItem) -> IsDropped {
	//pub fn on_drop_item(self, message: DropItem, bang: &mut Bang) -> IsDropped {
        //message.bang can be used here...
		println!("drops weight {}", self.weight);

		IsDropped {}
	}

	pub fn on_take_item(self, _: TakeItem) -> IsTaken {
		self
	}

}

impl IsDropped {
	pub fn on_take_item(self, _: TakeItem) -> IsTaken {
		let weight = 24;//@fixme set real value here
		println!("takes weight {}", weight);

		IsTaken {
			weight
		}
	}

	pub fn on_drop_item(self, _: DropItem) -> IsDropped {
		self
	}
}

impl Weight {
	fn new() -> Self {
		Weight::IsDropped(IsDropped{})
	}

	fn step(self, state: State, bang: &mut Bang) -> Weight {
		let next = match state {
			State::TakeItem{} => {
				self.on_take_item(TakeItem)
			},
			State::DropItem => {
				self.on_drop_item(DropItem{ bang })
			},
		};
		if next == Weight::error()
		{
            //reset machine
			return next.on_drop_item(DropItem{ bang })
		}
		next
	}
}

struct Bang {}

fn main() {
	let mut t = Weight::new();
	assert_eq!(t, Weight::isdropped());

	let mut bang = Bang {};

	t = t.step(State::TakeItem, &mut bang);
	assert_eq!(t, Weight::istaken(24));

	t = t.step(State::TakeItem, &mut bang);
	assert_eq!(t, Weight::istaken(24));

	t = t.step(State::DropItem, &mut bang);
	assert_eq!(t, Weight::isdropped());

	t = t.step(State::DropItem, &mut bang);
	assert_eq!(t, Weight::isdropped());
}

Thank you

1 Like

Apparently manually implementing an enum helps.

pub struct DropItem<'a> {
	bang: &'a mut Bang,
}

pub enum WeightMessages<'a> {
	DropItem(DropItem<'a>),
	TakeItem(TakeItem),
}

This works, but I have to write everything by hand, so I guess this is something the lib could support in future. I'll file a ticket...

2 Likes

Looking at the source code of the crate, they parse the message element for each transition as an identifier; there is no parsing of generic parameters such as lifetimes, which therefore cannot be added in the generated code.

They could add support for parsing lifetimes parameters (at least one, since 99% of the time there aren't more), so that you could be able to write:

transitions!(Weight,
  [
    (IsTaken, DropItem<'a>) => IsDropped,
    (IsDropped, DropItem<'a>) => IsDropped, 
    (IsDropped, TakeItem) => IsTaken,
    (IsTaken, TakeItem) => IsTaken
  ]
);

or something like that.

1 Like

Thank you. Here is the issue.

Hello,

Geal has updated the library in this commit. But I'm still using a fork because I'm having this struct:

pub struct TakeItem<'a> {
	a: &'a mut Object,
	b: &'a mut Object,
	c: &'a mut dyn Component,
}

Using this I get many errors like

binary operation `==` cannot be applied to type `components::add_weight::TakeItem<'_>`
binary operation `!=` cannot be applied to type `components::add_weight::TakeItem<'_>`
components::add_weight::TakeItem<'_>` doesn't implement `std::fmt::Debug`
the trait bound `components::add_weight::TakeItem<'_>: std::clone::Clone` is not satisfied

and obviously(?) I can't implement clone for TakeItem<'a>. That's why I removed the clone-requirement in my fork, but obviously (again?) I don't want to maintain a fork.

--- a/src/lib.rs
+++ b/src/lib.rs
@@ -724,7 +724,7 @@ pub fn transitions(input: proc_macro::TokenStream) -> syn::export::TokenStream {
 
     // define the state enum
     let toks = quote! {
-      #[derive(Clone,Debug,PartialEq)]
+      #[derive(Debug,PartialEq)]
       pub enum #message_enum_ident #type_arg_toks {
         #(#variants_names(#structs_names)),*
       }

Now I also need to implement PartialEq and Debug for every message (!)

impl std::fmt::Debug for DropItem<'_> {
	fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
		write!(f, "DropItem")
	}
}

impl Eq for DropItem<'_> {}

impl PartialEq for DropItem<'_> {
	fn eq(&self, other: &Self) -> bool {
		self.a == other.a && self.b == other.b
	}
}

(btw, are these correct implementation of PartialEq and Debug?) With these details, it works.

Obviously (yet again?) I don't want to use my fork. Is there any other way out of this?

Ultimately I'm looking for solution that lets me do this. Component is a trait. Object is a struct.

#[derive(Clone, Debug,PartialEq)]
pub struct DropItem<'a> {
	object: &'a mut Object,
	player: &'a mut Object,
	component: &'a mut dyn Component,
} 

Thanks.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.