I need an interface where I take a bunch of functions handling different types and I need to know what type they handle. Note I didn't say "return", because I'd really like to use references, not owned data (for performance, as this actually might be a fairly hot path: I'd love to avoid unneeded allocations here). Still, returning owned data is probably a valid way out if it helps.
In a completely ideal world, I'd have something like:
// free functions or methods, I'm fine either way (I probably don't need closures)
fn handle_i32(&blah) -> i32 { ... }
fn handle_i32_again(&blah) -> i32 { ... } // the types aren't unique
fn handle_str(&blah) -> &str { ... } // this does not borrow from args
fn handle_str_slice(&blah) -> &[str] { ... }
// in a trait impl
const HANDLERS = [
handle_i32.with_name("sample.i32"),
handle_i32_again.with_name("sample.another_i32"),
handle_str.with_name("sample.str"),
handle_str_slice.with_name("sample.str_slice"),
];
with some introspection, letting me know that HANDLERS[0].TYPE_ID == TypeId::Number
and HANDLERS[3].TYPE_ID == TypeId::String && HANDLERS[3].IS_LIST == true
.
I cannot use the return value for introspection, since I cannot really return references to locals, so more realistically, the handler functions would look like:
fn handle_str_slice(&blah, out: Takes<&[&str]>) {
out.emit(&["foo", "bar"])
}
(instead of returning a reference, pass it to something that can do the final processing step).
Unfortunately, as I have learned, I cannot handle all this by a trait implemented on FnMut(..., Takes<T>)
, because there may be multiple implementations.
Is there a way to detect the handled type while keeping the const HANDLERS
part as clean as possible? That will be written by the user of my library and I want minimal friction in there. I'm trying to avoid proc macros so the API remains fairly discoverable with an IDE.