Can I store a rusqlite Connection and Transaction side by side within my Struct?

As part of a small library I am writing, I have a Sturct "DataBase" that provides a thin wrapper around rusqlite:

pub struct DataBase {
    name: String,
    config_file: String,
    conn: Connection,
    cache: HashMap<String, CachedTable>,
    batch_size: usize,
    cache_size: usize,
}

impl DataBase {
    pub fn new(db_name: &str, config_file_name: &str) -> Self {
        let db_conn = Connection::open(db_name).unwrap();
        let mut db = DataBase {
            name: db_name.to_owned(),
            config_file: config_file_name.to_owned(),
            conn: db_conn,
            cache: HashMap::new(),
            batch_size: 50,
            cache_size: 0,
        };

        db.conn
            .execute_batch(
                "PRAGMA journal_mode = OFF;
                  PRAGMA synchronous = OFF;
                  PRAGMA temp_store = MEMORY;
                  PRAGMA cache_size = 1000000;
                  PRAGMA locking_mode = EXCLUSIVE;",
            )
            .expect("PRAGMA");

        db.initialize_cache().unwrap();

        db
    }

This works as a prototype but I need to find a way work with a rusqlite Transaction rather than the Connection. However, when one creates a Transaction with let tx = conn.transaction().unwrap();, the Connection Struct is "consumed" by the transaction method and can no longer be easily stored in a struct or passed/borrowed further on.

The one way around I can think of is to create the Connection and Transaction Structs outside my own Struct and pass a reference to the Transaction when creating a new Struct.

As I am fairly new to Rust, I have the following question: is this the only (and correct) solution to this problem or am I missing a trick?

No, this is not possible. It creates a self-referential struct, and that type of struct is not supported by the borrow checker.

You will have to make transaction in separate call from call that makes the connection, and if you want to wrap transaction in your own struct, it has to be a brand new type separate from type holding the connection.

1 Like

Thanks for confirming - that was my (novice) understanding. So I'll try to replace the Connection completely with the Transaction and pass the latter to my struct upon calling the ::new function.

You should copy rusqlite's approach and have two separate DataBase and DataBaseTransaction<'borrowed_from_database> structs.

The DataBaseTransaction struct can contain a reference back to DataBase if you need to (just not the other way).