[Closed] Need help with returning one of a few functions from another function

I am just learning rust, coming from Python. I am trying to write a Modbus Server as my first project. In Modbus, a client can request data of certain types through the use of a function code field. I am trying to have a function that reads the function code field from the request and returns the function which can handle that function code. That function is called get_modbus_function in the lib.rs file linked below. I have functions implemented for two of the possible function codes: read_coils and read_holding registers.

The problem I am having is understanding how to make this generic in order to handle the different types of read_coils and read_holding_registers.

Here is the compiler errors:

Compiling playground1 v0.1.0 (file:///home/keith/code/rust-playground)
src/lib.rs:84:32: 84:42 error: mismatched types:
 expected `fn(ReadRequest, &collections::vec::Vec<T>) -> core::result::Result<U, ModbusError>`,
    found `fn(ReadRequest, &collections::vec::Vec<bool>) -> core::result::Result<ModbusResponse<bool>, ModbusError> {read_coils}`
(expected type parameter,
    found bool) [E0308]
src/lib.rs:84         READ_COILS => Box::new(read_coils),
                                             ^~~~~~~~~~
src/lib.rs:84:32: 84:42 help: run `rustc --explain E0308` to see a detailed explanation
src/lib.rs:85:44: 85:66 error: mismatched types:
 expected `fn(ReadRequest, &collections::vec::Vec<T>) -> core::result::Result<U, ModbusError>`,
    found `fn(ReadRequest, &collections::vec::Vec<u16>) -> core::result::Result<ModbusResponse<u16>, ModbusError> {read_holding_registers}`
(expected type parameter,
    found u16) [E0308]
src/lib.rs:85         READ_HOLDING_REGISTERS => Box::new(read_holding_registers),
                                                         ^~~~~~~~~~~~~~~~~~~~~~
src/lib.rs:85:44: 85:66 help: run `rustc --explain E0308` to see a detailed explanation
    src/lib.rs:86:23: 86:33 error: mismatched types:
     expected `fn(ReadRequest, &collections::vec::Vec<T>) -> core::result::Result<U, ModbusError>`,
        found `fn(ReadRequest, &collections::vec::Vec<bool>) -> core::result::Result<ModbusResponse<bool>, ModbusError> {read_coils}`
    (expected type parameter,
        found bool) [E0308]
src/lib.rs:86         _ => Box::new(read_coils)
                                    ^~~~~~~~~~
src/lib.rs:86:23: 86:33 help: run `rustc --explain E0308` to see a detailed explanation
error: aborting due to 3 previous errors
Could not compile `playground1`.

I am not sure if this is possible or not. I have read the book and tried searching online. I am not exactly sure what to search for. Any pointers would be greatly appreciated.

Repository

The core issue is that you can't have definitions of types depend on data which is only available at runtime. This is a concept aptly named dependent typing, and Rust currently doesn't have it.

You're trying to return one of two different function types from get_modbuf_function():

  • fn(ReadRequest, &Vec<bool>) -> ModbusResult<ReadCoilResponse>
  • fn(ReadRequest, &Vec<u16>) -> ModbusResult<ReadHoldingRegisterResponse>

Syntatically, it might make sense to you to collapse these as type parameters. But the problem here is that type parameters on a function are so-called input types; they're chosen by the caller, either explicitly or via inference at the call site. You don't have control over them. A user of this API might try to call get_modbus_function::<String, TcpStream>(255) but the compiler has no idea how to coerce the function pointers returned to fn(ReadRequest, &Vec<String>) -> ModbusResult<TcpStream>.

All you can really do is match on function_code at a higher level and call read_coil() or read_holding_registers() at the same time. If it's any consolation, in the new form the compiler will be more effective at optimizing the relevant code, as function pointers represent a boundary that is rather difficult to optimize across.

As a side note, you don't need to put fn(...) -> ... types in a Box because they're already pointers themselves. You only need to Box closures (made with the |<argument,*>| <expression | block> syntax), to erase their compiler-generated types and collapse them to trait objects, i.e. Box<Fn(...) -> ...>, Box<FnMut(...) -> ...> and Box<FnOnce(...) -> ...> (which needs some hackery to actually work). Of course, a boxed function pointer can also be coerced to a closure trait object so that they can both fit in the same space, e.g. Box<fn(A, B) -> C> will coerce to Box<Fn(A, B) -> C> so they can fit in the same variable binding/struct field/etc.

Thanks for the response, I was wondering if I was trying to do something that wasn't allowed.