Here is a simple function that uses sqlx to update one field in one row of a SQLite database. It works.
struct MyDatabase {
conn: SqliteConnection,
rt: tokio::runtime::Runtime
}
fn update_field_plain(db: &mut MyDatabase, id: &str, column_name: &str, arg: i64) {
let task = async {
let sql = format!("update cards set {} = $1 where id = $2", column_name);
let q = sqlx::query(&sql);
q.bind(arg).bind(id).execute(&mut db.conn).await
};
let res = db.rt.block_on(task);
println!("update_field_plain, res = {res:?}");
}
Now, I think: I'd like to make this function work for any column type, so I make it generic:
fn update_field<T>(db: &mut MyDatabase, id: &str, column_name: &str, arg: T) { ... }
I understand why this is not enough. T
has requirements. The compiler complains:
q.bind(arg).bind(id).execute(&mut db.conn).await
---- ^^^ the trait `sqlx::Encode<'_, _>` is not implemented for `T`
And now I get lost. For a simple trait, I know to say where T: Trait
or <T: Trait>
, but there are multiple traits here, and a lifetime parameter, whose meaning I don't understand. The Rust Book says that lifetime parameters on a struct
means that the struct
can't outlive the contained references. Eg:
struct Thing<'a> {
some_ref: &'a str
}
But I don't remember anything about lifetimes on traits, and here, I imagine that an instance of Encode
doesn't "contain" a reference to anything. It's just something like i64
, String
, or &str
. I suppose the latter shows that it could be a reference. Still, I'm not sure what the lifetime parameter on Encode
means.
I end up with this, but I'm flailing, and it doesn't compile.
fn update_field<'q, T>(db: &mut MyDatabase, id: &str, column_name: &str, arg: T)
where T: sqlx::Encode<'q, Sqlite> + sqlx::Type<Sqlite>
{
// The body here is the same as the non-generic fn.
let task = async {
let sql = format!("update cards set {} = $1 where id = $2", column_name);
let q = sqlx::query(&sql);
q.bind(arg).bind(id).execute(&mut db.conn).await
};
let res = db.rt.block_on(task);
println!("update_field, r = {res:?}");
}
Error message, which I don't understand.
error[E0597]: `sql` does not live long enough
--> learning/sqlite/src/main.rs:325:29
|
320 | fn update_field<'q, T>(db: &mut MyDatabase, id: &str, column_name: &str, arg: T)
| -- lifetime `'q` defined here
...
324 | let sql = format!("update cards set {} = $1 where id = $2", column_name);
| --- binding `sql` declared here
325 | let q = sqlx::query(&sql);
| ------------^^^^-
| | |
| | borrowed value does not live long enough
| argument requires that `sql` is borrowed for `'q`
326 | q.bind(arg).bind(id).execute(&mut db.conn).await
327 | };
| - `sql` dropped here while still borrowed
What is this lifetime that is introduced by the Encode
generic bound? And what do I need to do satisfy it and get the function to compile?
~ Rob