Sqlite: caching prepared statements .... again

vitalyd https://users.rust-lang.org/u/vitalyd
February 14

donallen:

No, just the prepared statements that will be used many times. Can you

suggest a better way?

Well you need the connection as well since the statement is tied to it.

Yes. What I meant by that saying that you only the static at function scope
for the prepared statement. The connection needs to be visible to the whole
program somehow.

An approach I’d suggest is what you said later:

donallen:

I think you need to pass the Haskell equivalent

of a struct around that contains these values, capturing the struct in

callback closures where needed).

You’d set up your connections early on in main(), prepare statements that
you know a priori, and then hand out the statements as needed. These
instances would be kept around (and live) for the entirety of the program
and passed around as context/state where needed.

You mentioned gtk in the other post - does it require closures that you
give it to be 'static? If so, yeah, you’ll need to use Rc and RefCell. This
should be workable but you have to align your code structure/flow with
easier borrow checking. It’s a bit of a skill (comes with Rust experience)
in itself. I wrote a bit about some general strategies here: How can I
get myself out of this “mutable more than once” corner I’ve backed myself
into?
https://users.rust-lang.org/t/how-can-i-get-myself-out-of-this-mutable-more-than-once-corner-ive-backed-myself-into/15541.
Maybe that’ll help frame your approach a bit better.

By the way, have you looked at any of the connection pooling crates? Eg
r2d2_sqlite3 - Rust

donallen:

and the sqlite crates are not saying what they want to say, or

can’t say about the relationship between the lifetime of a connection and a

statement on which it depends.

I think the sqlite API says the correct thing about the relationship. A
statement depends on the connection you get it from. That seems pretty
uncontroversial.

Well now I'm thoroughly confused. In an earlier message, you said:

"fn prepare<'l, T: AsRef>(&'l self, statement: T) ->
Result<Statement<'l>>

As a result, the returned Statement has a lifetime parameter equal to the
borrow of self - note that it has nothing to do with how long the actual
self value lives, this is the lifetime of the borrow.

"

Your reading of what &'l self means -- that 'l is the lifetime of the
reference to self, which may be different from (less than) the lifetime of
self is exactly what I thought it meant. But your statement above ("I think
the sqlite API says the correct thing ...") made me wonder, because what is
necessary is that the statement not outlive the connection. Constraining
the statement to not outlive a reference to the connection is too
conservative, since the reference might die before the connection does,
which was the cause of the trouble with my thread_local attempt.

So I concocted a little example:

extern crate sqlite;

use sqlite::{Connection, Statement, State};

fn main() {
let db:Connection = Connection::open(":memory:").unwrap();
let mut stmt:Statement;
{
let inner_db = &db;
stmt = inner_db.prepare("select 12345").unwrap();

} //inner_db dies here

while let State::Row = stmt.next().unwrap() {
    println!("{}", stmt.read::<i64>(0).unwrap());
}

} //stmt dies here, just before db

To my utter amazement, this compiles and runs. The definition of prepare
says that the statement it returns will have the same lifetime as the
pointer to the connection it received, yes? Then how does this work?

This is so illustrative of the problem I have with Rust. Given the language
and the attempts to explain it, I simply do not have an accurate mental
model of what this thing is doing, which is essential, of course, if you
are going to use a programming language successfully. I understand the
memory models of C, Scheme, Haskell and every other programming language
I've used since I started doing this in ancient times. Rust consistently
violates the Principle of Least Surprise, at least for me.

Can you please explain to me why this example works?