现在我的代码使用rusqlite
sqlite bindings打开数据库连接并在我的应用程序中执行一系列数据库操作,如下所示:
extern crate rusqlite;
use rusqlite::SqliteConnection;
struct MyAppState {
db: SqliteConnection,
// ... pretend there's other fields here ...
}
impl MyAppState {
fn new() -> MyAppState {
let db = SqliteConnection::open(":memory:").unwrap();
MyAppState {
db: db
}
}
fn query_some_info(&mut self, arg: i64) -> i64 {
let mut stmt = self.db.prepare("SELECT ? + 1").unwrap();
let mut result_iter = stmt.query(&[&arg]).unwrap();
let result = result_iter.next().unwrap().unwrap().get(0);
result
}
}
fn main() {
let mut app = MyAppState::new();
for i in range(0, 100) {
let result = app.query_some_info(i);
println!("{}", result);
}
}
由于预处理语句存在于局部变量中,因此每次调用函数并生成局部变量时,我必须重新准备它,这似乎在某种程度上忽略了预处理语句的要点。理想情况下,我最多只准备一次所有语句,并在数据库连接期间将它们存储在MyAppState
结构中。
但是,自SqliteStatement
type is parameterized over the lifetime of the db connection以来,它借用了连接,并扩展了它所在的结构,我不再对结构做任何事情,比如按值返回struct或调用&mut self
方法在它上面query_some_info
并不需要在这里取&mut self
,但我的实际程序中的一些代码除非一切都继续存在于RefCell
中,这不是最差的,我猜,但仍然。)
通常当借阅检查员背叛我这样的时候,我的办法是放弃堆栈纪律并在这里和那里放一些Rc<RefCell< >>
,直到一切顺利,但在这种情况下,类型中有一些生命周期方式,我不知道如何以一种安抚借阅检查器的方式来表达它。
理想情况下,我想编写只在db打开时才准备语句的代码,或者可能只在第一次使用它们时准备一次,然后再在第二次使用期间再次调用prepare
。数据库连接,主要是保持rusqlite绑定的安全性,而不是针对sqlite3 C API编写代码或破坏抽象等等。我怎么样?
答案 0 :(得分:4)
你确实是对的,在Rust中,兄弟姐妹的引用很尴尬。但是有一个很好的理由,它们不容易被所有权制度建模。
在这种特殊情况下,我建议你拆分结构:你可以将准备好的语句保存在db
的生命周期中参数化的专用缓存 中; db
应该在程序的顶部实例化并传递下来(想想依赖注入),以便依赖它的缓存可以比程序主函数更长。
这确实意味着db
显然会被借用。
答案 1 :(得分:0)
Statement
结构具有生存期参数Statement<'conn>
。准备该语句时,必须引用一个超出该语句的Connection
。
extern crate rusqlite;
use rusqlite::{Connection, Statement};
struct MyAppState {
db: Connection,
}
impl MyAppState {
fn new() -> MyAppState {
let db = Connection::open(":memory:").unwrap();
MyAppState { db: db }
}
}
struct PreparedStatement<'conn> {
statement: Statement<'conn>,
}
impl<'conn> PreparedStatement<'conn> {
pub fn new<'a>(conn: &'a Connection, sql: &str) -> PreparedStatement<'a> {
PreparedStatement {
statement: conn.prepare(sql).unwrap(),
}
}
fn query_some_info(&mut self, arg: i64) -> i64 {
let mut result_iter = self.statement.query(&[&arg]).unwrap();
let result = result_iter.next().unwrap().unwrap().get(0);
result
}
}
fn main() {
let app = MyAppState::new();
let mut prepared_stmt = PreparedStatement::new(&app.db, "SELECT ? + 1");
for i in 0..100 {
let result = prepared_stmt.query_some_info(i);
println!("{}", result);
}
}
在Rust中,与某些其他语言不同,我发现将某些因素分解到函数中会改变其含义。它引入了新的生命周期,通常对您不利。但是在这种情况下,这正是需要的。