I'm trying to get the following code to compile, but rustc says that in transact
function variable tr
does not live long enough, and is dropped while being borrowed.
While I made this code to compile by either using macros or Arc's or moving the tr
into the callback and having the callback return it back, the code below seems to me to be the simplest and most elegant solution (except that it does not compile, lol).
What puzzles me the most is why rustc insists on the lifetime 'tr to live outside of my function?(otherwise it wouldn't complain that tr is dropped while being borrowed, right?)
I'm also not sure which way it works: the lifetime of variable tr in the body of transact defines the generic lifetime 'tr or the other way round?
use std::future::Future;
use anyhow::Result;
use async_trait::async_trait;
#[tokio::main]
async fn main() {
let db = TestDatabase;
transact(&db, |tx| tx.calculation()).await.unwrap();
}
pub async fn transact<'db, 'tr, Db, Fun, Fut, Res>(db: &'db Db, callback: Fun) -> Result<Res>
where
Db: Database,
Db::Transaction: 'tr,
Fun: FnOnce(&'tr mut Db::Transaction) -> Fut,
Fut: Future<Output = Result<Res>>,
Res: 'static, // marked this as static because I thought that there can be some relation between 'tr and result
{
let mut tr = db.begin().await?;
match callback(&mut tr).await {
Ok(res) => {
tr.commit().await?;
Ok(res)
}
Err(e) => {
tr.rollback().await?;
Err(e)
}
}
}
#[async_trait]
pub trait Database {
type Transaction: Transaction;
async fn begin(&self) -> Result<Self::Transaction>;
}
#[async_trait]
pub trait Transaction {
async fn commit(self) -> Result<()>;
async fn rollback(self) -> Result<()>;
}
struct TestDatabase;
#[async_trait]
impl Database for TestDatabase {
type Transaction = TestTransaction;
async fn begin(&self) -> Result<Self::Transaction> {
Ok(TestTransaction)
}
}
struct TestTransaction;
impl TestTransaction {
async fn calculation(&mut self) -> Result<i32> {
Ok(42)
}
}
#[async_trait]
impl Transaction for TestTransaction {
async fn commit(self) -> Result<()> {
Ok(())
}
async fn rollback(self) -> Result<()> {
Ok(())
}
}