Generics: Can I say "tuple where each element is FromSql"?

Hello! I am new to Rust and struggling with the compiler. I need to tell it that my function is generic over any tuple as long as each element satisfies the FromSql trait but don't know how to do that. I am trying to make a function to factor out these two similar pieces of code:

// somewhere above: extern crate oracle;
conn.query_as::<(String,)>("select A from ...", &[]);
...
conn.query_as::<(String,String,String)>("select X,Y,Z from ...", &[])

I have tried this:

fn sql_to_s3<T: FromSql>(sql: &str) -> Result<(), Box<dyn Error>> {
    // ...
    let rows = conn.query_as::<T>(sql, &[])?;

But this is not really what I need, what I need is to say something like "T is a tuple whose each element is FromSql". Is it possible? How?

Thank you!!!

Background

The signature of the query_as I need to call is

pub fn query_as<T: RowValue>(...

and the docs of oracle::RowValue read:

The trait was added to fetch column values as a tuple. The oracle crate provides implementations for a type implementing FromSql and tuples of types implementing FromSql. The number of elements in a tuple should be 1 through 50.

There is already impl FromSql for String. So I have everything necessary, and I just need to persuade the compiler :slight_smile:

Rust doesn't have variadic generics, so you can't express a concept of "any tuple". In Rust tuples aren't like arrays or lists, but more like structs without field names, so they don't have a length.

The current solution is to use a macro that repeats definition for specific tuples with certain number of elements, like 1 to 15.

1 Like

Another (more drastic) option is to go full frunk, and implement FromSql for HList (or similar), then you can basically get variadic generics with a different syntax.

2 Likes

Thank you! How would I do it here? Do I need to repeat the whole sql_to_s3, once using conn.query_as::<String> and once conn.query_as::<(String, String, String)> internally?

You can do something like this: Rust Playground

1 Like

Thanks a lot! I will study it :slight_smile:

For reference: this is my full code: https://gist.github.com/holyjak/742ada208fb16cd49295d5bc28bc7853

Solved: I can use T: RowValue instead since the oracle crate implements it for various types of tuples and it is the type the query_as method actually wants:

fn sql_to_s3<T: RowValue + Serialize>( ...

Though without that, using a macro as suggested would be the best solution.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.