I'm trying to implement a web app where a user can open a database transaction as part of an HTTP request handle, keep it open beyond the lifetime of that first request, then later commit the transaction. However, I'm running into a lifetime checking issue and, as a newbie, am not sure how to resolve it (Pin? GAT?). The core of the issue is demonstrated in the example below:
use core::marker::PhantomData;
struct DB {
}
impl DB {
fn transaction(&self) -> Transaction<'_, Self> {
Transaction {
_marker: PhantomData::default(),
}
}
}
struct Transaction<'db, TheDB> {
_marker: PhantomData<&'db TheDB>,
}
impl<'db, TheDB> Transaction<'db, TheDB> {
fn commit(self) -> () {
}
}
fn main() {
let mut state = State { tx: None, db: DB{} };
part1(&mut state);
part2(&mut state);
println!("ok");
}
struct State<'a> {
db: DB,
tx: Option<Transaction<'a, DB>>,
}
fn part1(state: &mut State) {
let tx = state.db.transaction();
state.tx = Some(tx);
}
fn part2(state: &mut State) {
match state.tx.take() {
Some(tx) => {
tx.commit();
},
None => {}
}
}
This fails to compile with:
error: lifetime may not live long enough
--> src/main.rs:37:5
|
35 | fn part1(state: &mut State) {
| ----- - let's call the lifetime of this reference `'1`
| |
| has type `&mut State<'2>`
36 | let tx = state.db.transaction();
37 | state.tx = Some(tx);
| ^^^^^^^^^^^^^^^^^^^ assignment requires that `'1` must outlive `'2`
In reality....
-
state
is stored as a web::Data object in actix-web -
DB
is arocksdb::TransactionDB
.
This would be trivial to accomplish if the tx
didn't have to be stored within State
and instead begins and ends within a single borrow of State
(during a single "part" of the above example).
What's the general approach to implementing something like this?