11

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.

5
  • This is the same problem as stackoverflow.com/q/20698384/155423 or 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 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. Commented Aug 25, 2015 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. Commented Aug 25, 2015 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. Commented Aug 25, 2015 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. Commented Aug 31, 2015 at 20:13
  • I had seen that other question — I doubt that, as that question was created 5 days after this one was. ^_^ Commented Aug 31, 2015 at 20:26

1 Answer 1

11

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.

Sign up to request clarification or add additional context in comments.

5 Comments

Aha! That's what I was missing. Thank you.
What would a sql connection + 2 prepared statements look like in actual code exactly?
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?
@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 Thanks. In this case (rusqlite Connection and Statement), Statement has a reference to the Connection, but doesn't expose it.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.