1

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...

asked Apr 26, 2024 at 8:06

1 Answer 1

3

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])?
 }
};
answered Apr 26, 2024 at 8:14
Sign up to request clarification or add additional context in comments.

Doh... this is a very good point. I had quite forgotten you could do "deferred initialization" in Rust. That's very elegant.

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.