How could I avoid repeating myself by passing the ToSql data as a closure, telling tokio-postgres which columns of data, from my various struct models, to COPY/SELECT/etc?
For instance, ideally I could pass various table/column args, like:
// see MRE, "copy_in" performs boilerplate to copy binary format in postgres table
copy_in(
&mut client, // the postgres client setup in tokio
"table_name", // meta info about what table/columns to insert
&["col1", "col2", "col3", "col4"],
&vec![Type::INT4, Type::VARCHAR, Type::INT4, Type::INT4],
&rows_of_struct_data, // the vec of data I plan to insert
// then here's the hard part: which fields to extract from each row... (not works)
|m| &[&[&m.id], &[&m.name], &[&m.range_begin, &m.range_end]] as &[&[&(dyn ToSql + Sync)]]
);
tokio_postgres requires a type for writing binary data during a copy, Vec<&(dyn ToSql + Sync)>, and though hardcoding what gets pushed into that Vec works, I am puzzled how to pass an argument that decides dynamically what to push into it.
I have two ideas for this, but what is recommended approach?
- As shown in this MRE, I could pass a closure to my copy function that returns which columns for each row to write to stdin, but how do I type closure to avoid?:
expected
&dyn ToSql + Sync, found trait objectdyn ToSql
- Alternatively, as shown in this MRE, I could pass the
Vecto my closure for it to push columns data into for each row, but that had several lifetime issues, like:
reference to
rowescapes the async closure body here
Out of my two approaches, I finally got #1 working, though I'm not accepting my own answer yet in case a better recommendation comes along.
Here is the working MRE
Here is what I was missing in my understanding:
ToSqltype was violated if a single Vec contained bothi32andStringInterested to see for purely functional programming learning what others might recommend. This is really helpful understanding how closures can be used. #2 approach has lifetime challenges I'm interested if others have insights on.