I've brought up this topic before and received some good suggestions. But I have never managed to come up with a satisfactory method for achieving something that is both useful and extremely simple in C: caching prepared sqlite statements that are used multiple times, to avoid unnecessary prepares.
In C, I simply use a static hold a pointer to a prepared statement. I initialized the static to NULL and in the function that uses the static (typically I define the static within the function if only that function will use it), I test if the pointer is NULL. If it is, I prepare the statement and store the result in the static. Done.
The application I am speaking about is single-threaded and always will be. In Rust, I'd love to be able to define a static with type &mut Option and initialize it to None. The first time I need the statement, I'd like to prepare it and set the static to Some(... prepared statement ...).
But I can't just use a mutable static even if I were willing to write unsafe code, because trying to initialize a mutable static of type Option to None won't work because None is not a value available at compile-time. lazy_static doesn't work because neither the sqlite or rusqlite crates implement Send for the Statement struct. lazy_static requires that the static types implement Sync. So my thought was to wrap the Option in a Mutex, which provides the Sync trait, but only if the type it enclosed implements Send, so this won't work. I think crate implementors are being conservative, since there are three options for the threading mode used to build sqlite and while the default is safe, I'm guessing they feel they have to code for the worst case since they have no control over the building of sqlite in the users' environment. They are probably right.
My application is single-threaded and always will be. Since there appears to be no way to say to Rust "look, I know you are all about thread safety, but I'm building a single-threaded application and your obsession with thread safety is driving me to distraction. Just back off, I know what I'm doing. Failing that, at Vitaly's suggestion of a month or two ago, I tried thread_local. This got me into lifetime hell. Thoroughly frustrated, I abandoned the effort in favor of other uses of my time. But I decided today to revisit this to try to construct a simple example that would illustrate the issue, hoping perhaps to either get a solution (it's certainly conceivable I'm overlooking something here) or improve the situation so this simple and useful requirement can be implemented in Rust as simply as I think should be possible.
Here's the code:
extern crate sqlite;
use sqlite::{Connection, Statement, State};
use std::cell::RefCell;
thread_local! {
static DB:Connection = Connection::open(":memory:").unwrap();
static TEST:RefCell<Statement<'static>> = RefCell::new(DB.with(|db| db.prepare("select 12345").unwrap()));
}
fn main() {
TEST.with(|test| {
let stmt = *(test.borrow_mut());
while let State::Row = stmt.next().unwrap() {
println!("{}", stmt.read::<i64>(1).unwrap());
}
});
}
Here's what happens when I try to build this:
dca@franz:~/Software/example$ cargo build > /tmp/errors
Compiling example v0.1.0 (file:///home/dca/Software/example)
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src/main.rs:8:76
|
8 | static TEST:RefCell<Statement<'static>> = RefCell::new(DB.with(|db| db.prepare("select 12345").unwrap()));
| ^^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime #2 defined on the body at 8:68...
--> src/main.rs:8:68
|
8 | static TEST:RefCell<Statement<'static>> = RefCell::new(DB.with(|db| db.prepare("select 12345").unwrap()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src/main.rs:8:73
|
8 | static TEST:RefCell<Statement<'static>> = RefCell::new(DB.with(|db| db.prepare("select 12345").unwrap()));
| ^^
= note: but, the lifetime must be valid for the static lifetime...
note: ...so that expression is assignable (expected sqlite::Statement<'static>, found sqlite::Statement<'_>)
--> src/main.rs:8:60
|
8 | static TEST:RefCell<Statement<'static>> = RefCell::new(DB.with(|db| db.prepare("select 12345").unwrap()));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to previous error
error: Could not compile `example`.
To learn more, run the command again with --verbose.
First of all, disappointingly, E0495 is not in the website's Rust Compiler Error Index. A fuller explanation of this error could perhaps have been helpful here.
Secondly, I have absolutely no clue what the compiler is complaining about. In the first line of the error details, "prepare" is underlined. What reference is outliving what borrowed content? Everything is static, or trying to be, so what's the problem?