Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Borrow problems with compiled SQL statements

My program uses rusqlite to build a database from another data source. The database builds multiple tables in the same manner, so I thought I'd make a reusable function to do so:

fn download_generic<Inserter>(table_name: &str,
                              connection: &mut rusqlite::Connection,
                              inserter: &mut Inserter)
                              -> Result<(), String>
    where Inserter: FnMut(&str, &json::JsonValue) -> ()
{}

inserter is a function that binds the correct values from a previously-prepared statement and does the insertion.

I call it like this:

let mut insert_stmt = connection
    .prepare("insert or replace into categories values(?,?);")
    .unwrap();

download_generic("categories",
                 &mut connection,
                 &mut |uuid, jsonproperties| {
                     insert_stmt.execute(&[&uuid, &jsonproperties["name"].as_str().unwrap_or("")]);
                 });

However I can't pass &mut connection to download_generic because it's already being borrowed by the insert_stmt. Putting it into a RefCell makes no sense because I shouldn't need runtime overhead to make this work.

I could try making the insert_stmt generated by a lambda that you pass to download_generic, but then I get overwhelmed by having to add lifetime markers everywhere, and it seems unnatural, anyway.

like image 262
njaard Avatar asked Oct 29 '22 13:10

njaard


1 Answers

By design, Rust prevents you from having an immutable borrow and a mutable borrow on the same object active at the same time. This is to prevent dangling pointers and data races.

In rusqlite's API, some methods on Connection require a mutable self, and some methods only require an immutable self. However, some of the methods that only require an immutable self return objects that keep that borrow active; prepare is an example of this. Therefore, as long as one of these objects stays in scope, Rust will not allow you to take a mutable borrow on the Connection.

There's probably a reason why some methods take self by mutable reference. Requiring a mutable reference ensures the callee that it has exclusive access to that object. If you think that might not be the case for the methods you need to use, or you think there could be another way to solve this, you should report an issue to the library's maintainers.

Regarding prepare specifically, you can work around the conflicting borrows by calling prepare_cached from within the closure instead. In order to do that, you'll have to make download_generic pass the connection back as a parameter to the closure, otherwise you'd have two mutable borrows on connection and that's not allowed.

like image 144
Francis Gagné Avatar answered Dec 14 '22 23:12

Francis Gagné