How to store rusqlite Connection and Statement objects in the same struct in Rust? [duplicate]
I'm working on my first Rust program and have run afoul of Rust ownership semantics. I have declared a struct
which will encapsulate a SQLite database connection so it maintains a Connection
member. For performance reasons, I also want to keep a prepared statement, represented by the Statement
type. Here is a simplified version of my code:
extern crate rusqlite; // 0.14.0
use rusqlite::{Connection, Statement};
pub struct Foo<'a> {
conn: Connection,
statement: Statement<'a>,
}
impl<'a> Foo<'a> {
pub fn new() -> Foo<'a> {
let conn = Connection::open(&":memory:").unwrap();
let statement = conn
.prepare("INSERT INTO Foo(name, hash) VALUES($1, $2)")
.unwrap();
Foo { conn, statement }
}
}
I'm trying to transfer ownership of the conn
variable to the callee by storing it in a member of Foo
, but when I attempt to compile this code it fails:
error[E0597]: `conn` does not live long enough
--> src/main.rs:13:25
|
13 | let statement = conn
| ^^^^ borrowed value does not live long enough
...
17 | }
| - borrowed value only lives until here
|
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 10:6...
--> src/main.rs:10:6
|
10 | impl<'a> Foo<'a> {
| ^^
For some reason, the rusqlite::Connection
type doesn't take a lifetime parameter, so I'm unable to explicitly tie its lifetime to that of the Statement
instance.
What am I missing? This kind of encapsulation is a very common pattern, I'm sure I'm missing something.
Let's look at Connection::prepare
:
pub fn prepare<'a>(&'a self, sql: &str) -> Result<Statement<'a>>
If we ignore the Result
(which just means that this function can fail), this means "return a Statement
that can live no longer than the Connection
that prepare
was called on". This is likely due to the Statement
containing a reference to the Connection
.
However, if you have a reference to an item, then you can no longer move the item because the reference would be invalidated. Using that invalid reference would lead to memory unsafety, so it's prevented.
Basically, you need to mirror the lifetimes and ownership of these objects in your code, and so you cannot bundle the Connection
and Statement
in the same structure. Instead, one can reference the other.