Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to re-use an sqlx::Executor?

Tags:

rust

rust-sqlx

I'm trying to develop an app server in Rust using SQLX for back-end storage. It's for a primitive CMS with some idiosyncratic default behavior, to wit: if the page is not found, generate it automatically with some blank content. I want to be able to re-use the functions that invoke the SQL, so I want them to take types implementing the sqlx::Executor trait, including &mut sqlx::Connection, &mut sqlx::pool::PoolConnection, and &mut sqlx::Transaction.

Every function with a single call to the database works great!

However, it seems like I can't re-use an executor. This function is called after I've determined the page does not exist; I create a transaction and call it with an &mut Transaction as the e argument:

async fn create_page_for_title(e: impl Executor<'_, Database = Sqlite>, title: &str) -> SqlResult<RawPage> {
    let newcontent_id = insert_new_root_content(e).await?;
    let newpage = insert_new_page(e, title, newcontent_id).await?;
    Ok(newpage)
}

And this is what Rust (1.46) says:

Error[E0382]: use of moved value: `e`
   --> src/store.rs:239:30
    |
230 | async fn create_page_for_title(e: impl Executor<'_, Database = Sqlite>, title: &str) -> SqlResult<RawPage>
    |                                - move occurs because `e` has type `impl Executor<'_, Database = Sqlite>`, which does not implement the `Copy` trait
...
238 |     let newcontent_id = insert_new_root_content(e).await?;
    |                                                 - value moved here
239 |     let newpage = insert_new_page(e, title, newcontent_id).await?;
    |                                   ^ value used here after move

I know e is a reference, and references are Copy. But I seem to be unable to convince Rust of this, and I'm not sure why. This is the only function where I use an impl Executor twice in the same scope, but I'm sure I'll discover more as time goes on. Please help?

like image 507
Elf Sternberg Avatar asked Feb 15 '26 14:02

Elf Sternberg


1 Answers

Since (as you've correctly noticed) references are Copy and the Executor trait is actually only implemented for reference types, as can be seen in the sqlx documentation, you can safely add + Copy trait bound to your function's declaration, so that it compiles now:

async fn create_page_for_title(e: impl Executor<'_, Database = Sqlite> + Copy, title: &str) -> SqlResult<RawPage> {
    let newcontent_id = insert_new_root_content(e).await?;
    let newpage = insert_new_page(e, title, newcontent_id).await?;
    Ok(newpage)
}

Or alternatively, if you find this version more readable:

async fn create_page_for_title<'e, E>(e: E, title: &str) -> SqlResult<RawPage>
where
    E: Executor<'e, Database = Sqlite> + Copy,
{
    let newcontent_id = insert_new_root_content(e).await?;
    let newpage = insert_new_page(e, title, newcontent_id).await?;
    Ok(newpage)
}
like image 143
m4tx Avatar answered Feb 17 '26 03:02

m4tx



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!