Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to store rusqlite Connection and Statement objects in the same struct in Rust? [duplicate]

Tags:

rust

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.

like image 427
anelson Avatar asked Aug 25 '15 16:08

anelson


1 Answers

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.

like image 57
Shepmaster Avatar answered Oct 02 '22 03:10

Shepmaster