Does Rust support closures with generic return types? For example, I want to write something like this:
let get<T: FromValue> = |s: &str| -> Option<T> { ... }
But that syntax is clearly wrong.
What I'm trying to do
I'm working with rust-mysql-simple, and I'm writing a from_row
method for my User
struct, to build a user from a database row.
The library doesn't provide (as far as I can tell) a way to look up query result row values by column name. So to work around this, my method looks like (this compiles and works correctly):
fn from_row(result: &QueryResult, row: Vec<Value>) -> User { let mut map: HashMap<_, _> = row.into_iter().enumerate().collect(); let mut get = |s: &str| { result.column_index(s) .and_then(|i| map.remove(&i) ) }; User { id: get("id").and_then(|x| from_value_opt(x).ok() ) } }
Here, result
is an object that contains information about the query's column names (used to find the column index for a column name), and the row
contains the ordered values from a query result row. from_value_opt
is a method provided by the library that takes a Value
and returns a Result<T, MyError>
. The value is coerced to the field's type.
I was trying to move the .and_then(|x| from_value_opt(x).ok() )
into the get
closure just to clean up the code some. However, when I do so, the closure return type is interpreted to be the result of the first occurrence of the get
call.
I rewrote the closure as a nested method that looks like:
fn get<T: FromValue>(r: &QueryResult, m: &mut HashMap<usize, Value>, s: &str) -> Option<T> { ... }
which also worked fine, but didn't help cutting the verbosity much.
No, AFAIK you can't. I mean, you can define a generic closure, what you can't do is create a let binding with a generic left-hand side.
A fn get<T>
, as the one you mention rewriting, undergoes monomorphisation, i.e. when compiling it, rustc generates a different version of get
for every actual T
that is used to call it. By the time you assign the result of that get
(let a = get(...)
), that result has a concrete type and size.
A let
binding does not get monomorphised, so you can't have a let a<T> = ...
and have a different version of a
generated for you by the compiler.
What I think might enable this is the introduction of higher-kinded types, which is one of the highly desired but not yet fully fleshed out new features for Rust. They would enable you to write something like:
// does not work as of Rust 1 let a = for<T> |s: &str, t: T| {...}
i.e. return a closure that I can later parametrize with a T (which is what you're asking for).
The closure type is anonymous, so you won’t be able to write it down, and seems that the compiler won’t be able to infer it, so no luck.
But is there any particular reason you want to use a closure? If I understood your question correctly, you just use this function to factor out some repeating actions, and you are not actually going to pass it around. Thus, an inner fn
should work just fine. The downside is that you’ll have to pass all the values that used to be automatically captured by your closure.
It would be something like that (the example is still pretty complex, so I didn’t try to compile it):
fn from_row(result: &QueryResult, row: Vec<Value>) -> User { let mut map: HashMap<_, _> = row.into_iter().enumerate().collect(); fn get<T: FromValue>(s: &str, result: &QueryResult, map: &mut HashMap<_, _>) -> T { result.column_index(s) .and_then(|i| map.remove(&i)) .and_then(|x| from_value_opt(x)).ok() }; User { id: get("id", &result, &mut map) } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With