借用编译的SQL语句的问题

时间:2017-01-15 19:25:33

标签: rust borrow-checker

我的程序使用rusqlite从另一个数据源构建数据库。数据库以相同的方式构建多个表,所以我认为我会创建一个可重用的函数:

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

inserter是一个函数,用于绑定先前准备的语句中的正确值并进行插入。

我称之为:

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("")]);
                 });

但是我无法将&mut connection传递给download_generic,因为它已经被insert_stmt借用了。将它放入RefCell没有任何意义,因为我不需要运行时开销来完成这项工作。

我可以尝试使用传递给insert_stmt的lambda生成download_generic,但是后来我不得不在任何地方添加生命周期标记而感到不知所措,无论如何它似乎都不自然。

1 个答案:

答案 0 :(得分:2)

根据设计,Rust会阻止您在同一个活动的同一对象上进行不可变借用和可变借用。这是为了防止悬空指针和数据争用。

在rusqlite的API中,Connection上的某些方法需要可变self,而某些方法只需要不可变self。但是,有些方法只需要一个不可变的self返回对象来保持激活; prepare就是一个例子。因此,只要其中一个对象停留在范围内,Rust就不允许您对Connection进行可变借用。

这可能是某些方法通过可变引用获取self的原因。需要可变引用可确保被调用者具有对该对象的独占访问权。如果您认为可能不是您需要使用的方法的情况,或者您认为可能有另一种解决方法,则应向库的维护人员报告问题。

具体而言,关于prepare,您可以通过在闭包内调用prepare_cached来解决冲突的借用。为了做到这一点,你必须让download_genericconnection作为参数传递给闭包,否则你在connection上有两个可变借词这是不允许的。