I'm creating a API function which has two parameters, I want the type of second parameter depends on the value of first one, for example:
enum Component{
Cpu,
Disk
}
struct CpuState {
pub frequency: u64
}
struct DiskState {
pub speed: u64
}
type Handler<T> = Box<dyn Fn(T) -> Box<dyn Future<Output = String> + Send> + Send + Sync>;
// the type of parameter(`<T>`) in handler is depends on the component you chosen.
fn monitor(component: Component, handler: Handler){}
The way I have tried is using trait but doesn't work:
trait StateEventHandler {
type Component;
type Handler;
}
impl StateEventHandler for CpuState {
type Component = Component::Cpu; // error: expected type, found variant `Component::Cpu`
not a type
type Handler = Handler<CpuState>;
}
impl StateEventHandler for DiskState {
type Component = Component::DiskState; // error like above
type Handler = Handler<DiskState>;
}
fn monitor<T: StateEventHandler>(component: T::Component, handler: T::Handler){}
How to do that?
Component::Cpu
and Component::Disk
are enum variants of Component
, not individual types. And therefore, you cannot declare them as associated types on the impl
s of StateEventHandler
.
The broad reason why this is not allowed is, which specific enum variant was passed by the user is an information only known at runtime, but generic monomorphization happens at compile time. How do you "set" a compile time type requirement based on runtime information? You can't.
Probably you should just declare two unit structs like so:
struct CpuComponent;
struct DiskComponent;
impl StateEventHandler for CpuState {
type Component = CpuComponent;
type Handler = Handler<CpuState>;
}
impl StateEventHandler for DiskState {
type Component = DiskComponent;
type Handler = Handler<DiskState>;
}
That's how to express the semantics you need.
If you still need an enum over CpuComponent
and DiskComponent
elsewhere, you can do that too. std::net::IpAddr
is a good example. strum::EnumDiscriminants
may also come in handy.