I'm looking for advice from experienced Rust users on how best to use the type system to express a certain set of constraints.
I have several kinds of actions my system supports, so I have an enum to represent them:
enum ApiAction {
SetFoo,
GetFoo,
GetBar,
ResetAll,
}
I pass this around in various circumstances to identify which action my system is handling.
I also have certain inputs needed (different inputs for each action). At first I created a struct to hold the input values:
struct ApiRequest {
foo_id: Option<String>,
new_foo_value: Option<String>,
bar_id: Option<String>,
}
But I think I can do better! I can create an enum where each instance contains only the required. That's nifty, because now I can parse the input, then attempt to create an instance of ApiRequest and lower levels of my code which receive an ApiRequest object can be assured that all the required fields were present because otherwise we'd have reported an issue during parsing. It looks like this:
enum ApiRequest {
SetFoo{foo_id: String, new_value: String},
GetFoo{foo_id: String},
GetBar{bar_id: String},
ResetAll,
}
But I have one problem. When Rust creates an enum, it creates a discriminant to identify the values. In the case of this ApiRequest enum, the discrimant SHOULD be my ApiAction enum. As shown, there is nothing other than a naming convention that shows that ApiRequest::SetFoo is associated with ApiAction::SetFoo. I can't easily have the compiler verify that when the action is SetFoo the request is ALSO SetFoo.
Is there a straightforward and idiomatic way to do this?