Why can't I pass through this &mut borrow, twice?

I created a sqlx::SqliteConnection and was trying to pass it to some functions to synchronously query a DB. It worked but the following doesn't compile, because the second use of conn is an error: "value used here after move"

Logically, it seems okay. I have an exclusive borrow, and I want to use it two times in succession, so they don't overlap. How do I adjust this code to say this?

use sqlx::SqliteConnection;
use tokio::runtime::Runtime;

fn twice(conn: &mut SqliteConnection, rt: &Runtime) {
    let task = async {
        let rows1 = sqlx::query("select * from cheese").fetch_all(conn).await?;
        let rows2 = sqlx::query("select * from breads").fetch_all(conn).await?;
        Ok::<_, sqlx::Error>(())
    };
    rt.block_on(task);
}

This is a subtle issue with type inference and automatic reborrowing.

The fetch_all method in question takes some executor: E argument where E: Executor<…>, and that trait in turn is implemented for certain mutable references. But type inference works in such a way that if the type E isn’t known a priori, then most possibilities for coercions of function arguments are eliminated to get rid of ambiguities. This way, once the compiler can deduce finally that E has to be the exact type of conn, a mutable-reference type in this case, (and this is just my intuition as a user; I don’t actually knowing anything about the relevant compiler internals at play here) it seems that by that point in some sense, the implicit reborrowing[1] can’t be inserted anymore.

Implicit reborrowing? That’s if you pass some value x: &mut T to a function f that expects a &mut T argument! In that case, even though the type &mut T doesn’t implement Copy, passing it like f(x) doesn’t move the reference of x, but instead creates a potentially-shorter-lived reborrow.

Making reborrowing explicit instead would look like f(&mut *x) instead. Reborrowing is a fancy word for “taking a new reference to the target of another reference”, if you will.

And that’s what you can do to solve the issue here, too:

let rows1 = sqlx::query("select * from cheese").fetch_all(&mut *conn).await?;

like this it should work. Alternatively, you can give a (at least partial) type hint to take (some of the) type inference out of the equation: e.g. something like

let rows1 = sqlx::query("select * from cheese").fetch_all::<&mut _>(conn).await?;

should probably work, too.

A typical minimal example that shows the same kind of mechanism would be:

let x = &mut 42;
let y = x;
// use x
println!("{x}"); // error, use of moved value

vs

let x = &mut 42;
let y: &mut _ = x; // <- added a (partial) type hint
// use x
println!("{x}"); // this works now, x was only reborrowed

  1. which is probably somewhat comparable to a coercion – that’s why I mentioned those in the previous sentence ↩︎

9 Likes

Both work, thanks. I thought it was some beginner mistake, but no, this was trickier.