10

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
anelson
  • 2,569
  • 1
  • 19
  • 30
  • This is the same problem as http://stackoverflow.com/q/20698384/155423 or http://stackoverflow.com/q/30538387/155423 or really any of the numerous questions that ask the question "how do I have a reference to 'foo' and then move 'foo'?". There was [this question from yesterday](http://stackoverflow.com/q/32194269/155423) as well. The short version is that once you have a reference into an item, you can't move that item because it would invalidate the reference. – Shepmaster Aug 25 '15 at 17:12
  • Thanks for the comment. Maybe I'm misunderstanding the nomenclature, but in my example I don't think I'm using references. I intend to transfer ownership of the `SqliteConnection` from the scope of the `new` function to the `Foo` struct, which itself is to be owned by the caller of the `new` function. – anelson Aug 25 '15 at 17:18
  • I'd recommend changing your title to something more specific, like "How do I have a `SqliteStatement` and `SqliteConnection` in the same struct". As it is now, your title isn't focused enough as transferring ownership to a struct is easy, but isn't what your problem is. – Shepmaster Aug 25 '15 at 17:28
  • If we define the best answer as the one that clarified for me exactly why what I was trying to do is not possible by design, then your answer to this question is definitely the best. I had seen that other question but I did not grasp the way in which what I was attempting was in effect the same thing. – anelson Aug 31 '15 at 20:13
  • *I had seen that other question* — I doubt that, as that question was created 5 days after this one was. ^_^ – Shepmaster Aug 31 '15 at 20:26

1 Answers1

10

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Aha! That's what I was missing. Thank you. – anelson Aug 25 '15 at 17:28
  • 5
    What would a sql connection + 2 prepared statements look like in actual code exactly? – Tommi Komulainen Sep 28 '16 at 18:22
  • How would you implement "one can reference the other"? Do I just need to have to variables with no connection in the code and pass both of them wherever I want the `Foo` from the original question? – Troy Daniels May 05 '21 at 20:53
  • @TroyDaniels two separate types, one not embedded in the other. You can usually pass just the one that contains the reference to the other, and access the referred-to item. – Shepmaster May 05 '21 at 23:59
  • @Shepmaster Thanks. In this case (rusqlite Connection and Statement), Statement has a reference to the Connection, but doesn't expose it. – Troy Daniels May 10 '21 at 21:31