Using DB connection in Struct

I'm trying to create a db connection, then pass it to a data gatherer struct, which would gather then use the conn to save the data. It seems I ended up in a complex situation of mutable borrowing and lifetime issues, which I can't find my way out. I created a small example -- I'm hoping someone can make this sense and compile for me:

use rusqlite::{named_params, Connection, Result, ToSql, Transaction};

fn main() -> Result<()> {
    let sqlite = Connection::open_in_memory()?;
    let dm = DataManager::new(sqlite);
    dm.save_data();
    Ok(())
}

struct DataManager {
    db_conn: Connection
}

impl  DataManager {
    pub fn new(conn: Connection) -> Self {
        DataManager { db_conn: conn }
    }

    fn save_data(&self) {
        let data = 20; //get data
        self.save_with_transaction(data, self.db_conn).unwrap();
    }

    fn save_with_transaction(&self, data: i32, conn: &mut Connection) -> Result<()> {
        let tx = conn.transaction()?;
        self.do_transaction(data, &tx)?;
        tx.commit();
        Ok(())
    }

    fn do_transaction<'a>(&self, data: i32, tx: &Transaction<'a>) -> Result<()> {
        let mut stmt = tx.prepare("INSERT INTO data (datapoint) VALUES (:datapoint)")?;
        let params: &[(&str, &dyn ToSql)] = named_params! {":datapoint": data};
        stmt.execute(params,)?;
        Ok(())
    }
}

Currently it fails with "cannot borrow self.db_conn as mutable because it is also borrowed as immutable" on self.save_with_transaction(data, self.db_conn).unwrap(); which I understand, but cannot fix. I tried to make the field mutable:

use rusqlite::{named_params, Connection, Result, ToSql, Transaction};

fn main() -> Result<()> {
    let mut sqlite = Connection::open_in_memory()?;
    let mut dm = DataManager::new(&mut sqlite);
    dm.save_data();
    Ok(())
}

struct DataManager<'a> {
    db_conn: &'a mut Connection
}

impl  DataManager<'_> {
    pub fn new(conn: &mut Connection) -> DataManager<'_> {
        DataManager { db_conn: conn }
    }

    fn save_data(&mut self) {
        let data = 20; //get data
        self.save_with_transaction(data, self.db_conn).unwrap();
    }

    fn save_with_transaction(&self, data: i32, conn: &mut Connection) -> Result<()> {
        let tx = conn.transaction()?;
        self.do_transaction(data, &tx)?;
        tx.commit();
        Ok(())
    }

    fn do_transaction<'a>(&self, data: i32, tx: &Transaction<'a>) -> Result<()> {
        let mut stmt = tx.prepare("INSERT INTO data (datapoint) VALUES (:datapoint)")?;
        let params: &[(&str, &dyn ToSql)] = named_params! {":datapoint": data};
        stmt.execute(params,)?;
        Ok(())
    }
}

but then I ended up with mutable and immutable borrow happening on the same line.

I'd appreciate any help from the experts.

Since the connection is in self.db_conn you don't need the conn parameter of save_with_transaction. Removing that parameter will avoid borrowing self twice when calling save_with_transaction, once mutably (this is never allowed in Rust).

That change may move the problem to the call to do_transaction. This can be solved by removing the self parameter of do_transaction, which is not used.

To avoid borrowing conflicts the solution is often to only pass the parameters that are needed.

2 Likes