Rusqlite `Connection` and `Statement` lifetimes issue

⚓ Rust    📅 2026-02-16    👤 surdeus    👁️ 1      

surdeus

I have the following code

let mut stmt = unwrap!(
  conn.prepare_cached("SELECT EXISTS(SELECT 1 FROM users WHERE name = ?1);"),
  |err| {
    return Err((conn, db::Error::Rusqlite(err)));
  },
);

let res = unwrap!(
  stmt.query_row([&name], |row| row.get::<_, bool>(0)),
  |err| {
    return Err((conn, db::Error::Rusqlite(err)));
  },
);

Ok((conn, res))

(unwrap! is just a shortcut for match x { Ok(x) => x, Err($err) => $action })

which basically runs the query and on success returns the connection to be reused and result, and on errors returns the connection and error.

The code does not compile

error[E0505]: cannot move out of `conn` because it is borrowed
   --> src/main.rs:449:33
    |
428 | async fn db_manager(mut cmd_rx: db::CmdRx, mut conn: Connection) {
    |                                            -------- binding `conn` declared here
...
447 |                   conn.prepare_cached("SELECT EXISTS(SELECT 1 FROM users WHERE name = ?1);"),
    |                   --------------------------------------------------------------------------
    |                   |
    |                   borrow of `conn` occurs here
    |                   a temporary with access to the borrow is created here ...
448 |                   |err| {
449 |                     return Err((conn, db::Error::Rusqlite(err)));
    |                                 ^^^^ move out of `conn` occurs here
450 |                   },
451 |                 );
    |                  - ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Result<CachedStatement<'_>, rusqlite::Error>`

error[E0505]: cannot move out of `conn` because it is borrowed
   --> src/main.rs:456:33
    |
428 | async fn db_manager(mut cmd_rx: db::CmdRx, mut conn: Connection) {
    |                                            -------- binding `conn` declared here
...
447 |                   conn.prepare_cached("SELECT EXISTS(SELECT 1 FROM users WHERE name = ?1);"),
    |                   ---- borrow of `conn` occurs here
...
456 |                     return Err((conn, db::Error::Rusqlite(err)));
    |                                 ^^^^ move out of `conn` occurs here
...
461 |               })
    |               - borrow might be used here, when `stmt` is dropped and runs the `Drop` code for type `CachedStatement`

error[E0505]: cannot move out of `conn` because it is borrowed
   --> src/main.rs:460:21
    |
428 | async fn db_manager(mut cmd_rx: db::CmdRx, mut conn: Connection) {
    |                                            -------- binding `conn` declared here
...
447 |                   conn.prepare_cached("SELECT EXISTS(SELECT 1 FROM users WHERE name = ?1);"),
    |                   ---- borrow of `conn` occurs here
...
460 |                 Ok((conn, res))
    |                     ^^^^ move out of `conn` occurs here
461 |               })
    |               - borrow might be used here, when `stmt` is dropped and runs the `Drop` code for type `CachedStatement`

For more information about this error, try `rustc --explain E0505`.
error: could not compile `db-server` (bin "db-server") due to 3 previous errors

so I add a few drop()s

let mut stmt = unwrap!(
  conn.prepare_cached("SELECT EXISTS(SELECT 1 FROM users WHERE name = ?1);"),
  |err| {
    return Err((conn, db::Error::Rusqlite(err)));
  },
);

let res = unwrap!(
  stmt.query_row([&name], |row| row.get::<_, bool>(0)),
  |err| {
    drop(stmt);
    return Err((conn, db::Error::Rusqlite(err)));
  },
);

drop(stmt);
Ok((conn, res))

but it still does not compile

error[E0505]: cannot move out of `conn` because it is borrowed
   --> src/main.rs:449:33
    |
428 | async fn db_manager(mut cmd_rx: db::CmdRx, mut conn: Connection) {
    |                                            -------- binding `conn` declared here
...
447 |                   conn.prepare_cached("SELECT EXISTS(SELECT 1 FROM users WHERE name = ?1);"),
    |                   --------------------------------------------------------------------------
    |                   |
    |                   borrow of `conn` occurs here
    |                   a temporary with access to the borrow is created here ...
448 |                   |err| {
449 |                     return Err((conn, db::Error::Rusqlite(err)));
    |                                 ^^^^ move out of `conn` occurs here
450 |                   },
451 |                 );
    |                  - ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `Result<CachedStatement<'_>, rusqlite::Error>`

For more information about this error, try `rustc --explain E0505`.
error: could not compile `db-server` (bin "db-server") due to 1 previous error

so I think of moving the call to a variable so that I can drop it in the error path

let stmt =
  conn.prepare_cached("SELECT EXISTS(SELECT 1 FROM users WHERE name = ?1);");

let mut stmt = unwrap!(stmt, |err| {
  drop(stmt);
  return Err((conn, db::Error::Rusqlite(err)));
});

let res = unwrap!(
  stmt.query_row([&name], |row| row.get::<_, bool>(0)),
  |err| {
    drop(stmt);
    return Err((conn, db::Error::Rusqlite(err)));
  },
);

drop(stmt);
Ok((conn, res))

different errors than the first attempt

error[E0382]: use of partially moved value: `stmt`
   --> src/main.rs:450:24
    |
449 |                 let mut stmt = unwrap!(stmt, |err| {
    |                                               --- value partially moved here
450 |                   drop(stmt);
    |                        ^^^^ value used here after partial move
    |
    = note: partial move occurs because value has type `rusqlite::Error`, which does not implement the `Copy` trait
help: borrow this binding in the pattern to avoid moving the value
    |
449 |                 let mut stmt = unwrap!(stmt, |ref err| {
    |                                               +++

error[E0505]: cannot move out of `conn` because it is borrowed
   --> src/main.rs:458:33
    |
428 | async fn db_manager(mut cmd_rx: db::CmdRx, mut conn: Connection) {
    |                                            -------- binding `conn` declared here
...
447 |                   conn.prepare_cached("SELECT EXISTS(SELECT 1 FROM users WHERE name = ?1);");
    |                   ---- borrow of `conn` occurs here
...
458 |                     return Err((conn, db::Error::Rusqlite(err)));
    |                                 ^^^^ move out of `conn` occurs here
...
464 |               })
    |               - borrow might be used here, when `stmt` is dropped and runs the destructor for type `Result<CachedStatement<'_>, rusqlite::Error>`

error[E0505]: cannot move out of `conn` because it is borrowed
   --> src/main.rs:463:21
    |
428 | async fn db_manager(mut cmd_rx: db::CmdRx, mut conn: Connection) {
    |                                            -------- binding `conn` declared here
...
447 |                   conn.prepare_cached("SELECT EXISTS(SELECT 1 FROM users WHERE name = ?1);");
    |                   ---- borrow of `conn` occurs here
...
463 |                 Ok((conn, res))
    |                     ^^^^ move out of `conn` occurs here
464 |               })
    |               - borrow might be used here, when `stmt` is dropped and runs the destructor for type `Result<CachedStatement<'_>, rusqlite::Error>`

Some errors have detailed explanations: E0382, E0505.
For more information about an error, try `rustc --explain E0382`.
error: could not compile `db-server` (bin "db-server") due to 3 previous errors

No matter what I tried I could not solve it. I do not understand why I cannot return conn in the error path as in that case nothing is borrowing it

5 posts - 2 participants

Read full topic

🏷️ Rust_feed