I have a situation where I wish to provide a struct field of a generic type. How that field is implemented is up to the user of the struct. However, I wish to require the user to consider that the type must consider something. Here's the approach I've taken:
Therefore, it is on the user to consider what type of C represents a next_event command.
The next_event function is not expected to be called from anywhere (at least presently). Its entire existence is to ensure that the particular command has been considered. Here's a sample implementation:
The linker should remove the implementation to next_event given the lack of use. So, goal achieved in terms of the user having to consider this command in their enumeration. However, I'm wondering if there's a better way?
I could also wrap all commands with something like the following:
pub enum Command<C> {
NextEvent,
User(C),
}
...but then I must consider custom serialisation and it is a level of indirection on the user.
If next_event() is never actually used and you just want to make people opt-in to some specific behaviour, why not make MandatoryCommands a "marker trait"?
/// By implementing this trait you guarantee that you have considered
/// X, Y, and Z.
trait MandatoryCommands {}
Otherwise if what you are really trying to express is some association between your C: MandatoryCommands and some other type, you can use an associated type.
trait MandatoryCommands {
type Command;
}
This requires that any C: MandatoryCommands has a C::Command type we can talk about (e.g. fn do_stuff<C>() where C: MandatoryCommands, C::Command: Clone).
From there, you can write your where clause as only accepting some type, C, where C's NextEventCommand is a NextEvent.
fn do_stuff<C>(...)
where
C: MandatoryCommand<NextEventCommand = NextEvent>,
{
...
}
Although maybe more context is needed. Requiring that an associated item is a particular enum variant is normally not what you want to do, and comes about when you mix up types and values.
Thanks for the follow up. I think the marker trait is sufficient. If the user is determined to ignore its contract then it’s on them. And there may be valid reasons for them to do so.