rusttuplesborrow-checkerrusqlite

How do I initialize two variables in match statement without angering borrow checker?


So I have this code which uses rusqlite. And it works fine.

pub struct Database {
    conn: Connection,
}

impl Database {
    pub fn get(self: &Self, id: Option<u64>, name: Option<&str>) -> Result<Option<Record>, Error> {

        let mut stmt = match name {
            Some(_) => { self.conn.prepare_cached(Self::STMT_GET_ID_NAME) },
            None => { self.conn.prepare_cached(Self::STMT_GET_ID) }
        }?;

        let mut rows = match name {
            Some(name) => { stmt.query(params![id, name]) },
            None => { stmt.query(params![id]) }
        }?;


        let row = rows.next()?;
        if let Some(row) = row {
            let record = Record { 
                id: row.get(0)?, 
                parent: row.get(1)?, 
                name: row.get(2)?, 
                record_type: row.get(3)?,
                timestamp: row.get(4)?, 
                created_at: row.get(5)?, 
                modified_at: row.get(6)?, 
                size: row.get(7)?, 
                hash: row.get(8)?, 
                inode: row.get(9)? 
            };
            Ok(Some(record))
        } else {
            Ok(None)
        }
    }
}

The problem I have is that the match statement is essentially duplicated.

I tried something like this but this won't work.

        let (mut stmt, mut rows) = match name {
            Some(name) => {
                let mut stmt = self.conn.prepare_cached(Self::STMT_GET_ID_NAME)?;
                let rows = stmt.query(params![id, name])?;
                (stmt, rows)
            }
            None => {
                let mut stmt = self.conn.prepare_cached(Self::STMT_GET_ID)?;
                let rows = stmt.query(params![id])?;
                (stmt, rows)
            }
        };

The problem is that the compiler complains about the lifetime of stmt

error[E0597]: `stmt` does not live long enough
error[E0505]: cannot move out of `stmt` because it is borrowed

I really tried a lot of things and try to google furiously. But I'm stuck.

Is there even a way to do this that is not utterly ugly and idiomatic? I feel like this is a really dumb problem, and I'm missing something fundamental here… 


Solution

  • The problem is that attempting to "return" both stmt and rows from the subexpression moves stmt and therefore invalidates references held by rows. To fix this, you can omit the inner stmt and assign directly to the outer one from your match arms. That way stmt never moves and the borrow checker is happy, so e.g. this compiles:

    let mut stmt;
    let mut rows = match name {
        Some(name) => {
            stmt = self.conn.prepare_cached(Self::STMT_GET_ID_NAME)?;
            stmt.query(params![id, name])?
        }
        None => {
            stmt = self.conn.prepare_cached(Self::STMT_GET_ID)?;
            stmt.query(params![id])?
        }
    };