You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
I'm working on a database transaction proxy system and encountering lifetime issues when trying to create a wrapper around async callbacks. I have a TransactionTrait that defines a transaction method taking an async callback, and I'm trying to create a proxy that can wrap and enhance these callbacks.
I have two attempts at creating a proxy callback function, but both have lifetime issues:
asyncfntransaction<F,T,E>(&self,callback:F) -> Result<T,TransactionError<E>>whereF:for<'c>FnOnce(&'cDatabaseTransaction,) -> Pin<Box<dynFuture<Output = Result<T,E>> + Send + 'c>>
+ Send,T:Send,E: std::fmt::Display + std::fmt::Debug + Send,{let callback_proxy:Box<dynfor<'c>FnOnce(&'cDatabaseTransaction,)
-> Pin<Box<dynFuture<Output = Result<T,E>> + Send + 'c>>
+ Send,> = Box::new(move |tx:&DatabaseTransaction| {Box::pin(asyncmove{// todo: do_something().await;// the parameter type `F` may not live long enough [E0310]// the parameter type `F` must be valid for the static lifetime...//How to add an explicit lifetime bound?????callback(tx).await})});self.inner.transaction(callback_proxy).await}
Attempt 1: proxy_callback1
asyncfnproxy_callback1<F,T,E>(callback:F,) -> implfor<'r>FnOnce(&'rDatabaseTransaction,) -> Pin<Box<dynFuture<Output = Result<T,E>> + Send + 'r>>
+ SendwhereF:for<'c>FnOnce(&'cDatabaseTransaction,) -> Pin<Box<dynFuture<Output = Result<T,E>> + Send + 'c>>
+ Send,T:Send,E:Display + Debug + Send,{move |dt:&DatabaseTransaction| {// ERROR: the parameter type `F` may not live long enough [E0310]// the parameter type `F` must be valid for the static lifetime..Box::pin(asyncmove{do_something().await;callback(dt).await})}}
Error: The compiler complains that type F may not live long enough and must be valid for the 'static lifetime.
Attempt 2: proxy_callback2
asyncfnproxy_callback2<'a,F,T,E>(callback:F,) -> implFnOnce(&'aDatabaseTransaction) -> Pin<Box<dynFuture<Output = Result<T,E>> + Send + 'a>>
+ Send
+ 'awhereF:FnOnce(&'aDatabaseTransaction) -> Pin<Box<dynFuture<Output = Result<T,E>> + Send + 'a>>
+ Send
+ 'a,T:Send,E:Display + Debug + Send,{move |dt:&DatabaseTransaction| {// This compiles but is too restrictiveBox::pin(asyncmove{do_something().await;callback(dt).await})}}
This version compiles but is too restrictive - it requires all lifetimes to be the same ('a ), which doesn't match the more flexible higher-ranked trait bounds (HRTB) used in the trait.
Attempt 3: Objective: Strengthen this closure
#[async_trait::async_trait]implTransactionTraitforDatabaseConnectionProxy{asyncfntransaction<F,T,E>(&self,callback:F) -> Result<T,TransactionError<E>>whereF:for<'c>FnOnce(&'cDatabaseTransaction,) -> Pin<Box<dynFuture<Output = Result<T,E>> + Send + 'c>>
+ Send,T:Send,E:Display + Debug + Send,{// Proxy callback:let callback_proxy = move |dt:&DatabaseTransaction| {//dt lifetime:'1// lifetime may not live long enough// returning this value requires that `'1` must outlive `'2`// the parameter type `F` may not live long enough [E0310]// the parameter type `F` must be valid for the static lifetime..// todo: How to add an explicit lifetime boundBox::pin(async{// Do something :eg: log.info().await;callback(dt).await// callback lifetime :'2})asPin<Box<dynFuture<Output = Result<T,E>> + Send + '_>>};self.inner_conn.transaction(callback_proxy).await}}
The Core Issue
I want to extract the proxy callback logic into a reusable function, but I can't seem to express the correct lifetime relationships. The trait uses HRTB (for<'c> ) to indicate that the callback should work for any lifetime 'c of the DatabaseTransaction , but when I try to create a standalone function, I either get lifetime errors or the signature becomes too restrictive.
What I've Tried
Higher-ranked trait bounds in the return type
Explicit lifetime parameters (but they become too restrictive)
Various combinations of + 'static bounds
Questions
Why does the inline closure work in the trait implementation but not when extracted to a function?
How can I express the proper lifetime bounds for a standalone proxy callback function?
Is there a way to maintain the flexibility of the HRTB while extracting this logic?
All Code
use std::fmt::{Debug,Display};use std::pin::Pin;pubenumTransactionError<E>{Connection,Transaction(E),}pubstructDatabaseTransaction;#[async_trait::async_trait]pubtraitTransactionTrait{asyncfntransaction<F,T,E>(&self,callback:F) -> Result<T,TransactionError<E>>whereF:for<'c>FnOnce(&'cDatabaseTransaction,) -> Pin<Box<dynFuture<Output = Result<T,E>> + Send + 'c>>
+ Send,T:Send,E:Display + Debug + Send;}pubstructDatabaseConnection;#[async_trait::async_trait]implTransactionTraitforDatabaseConnection{asyncfntransaction<F,T,E>(&self,callback:F) -> Result<T,TransactionError<E>>whereF:for<'c>FnOnce(&'cDatabaseTransaction,) -> Pin<Box<dynFuture<Output = Result<T,E>> + Send + 'c>>
+ Send,T:Send,E:Display + Debug + Send,{callback(&DatabaseTransaction).await.map_err(TransactionError::Transaction)}}pubstructDatabaseConnectionProxy{inner_conn:DatabaseConnection,}#[async_trait::async_trait]implTransactionTraitforDatabaseConnectionProxy{asyncfntransaction<F,T,E>(&self,callback:F) -> Result<T,TransactionError<E>>whereF:for<'c>FnOnce(&'cDatabaseTransaction,) -> Pin<Box<dynFuture<Output = Result<T,E>> + Send + 'c>>
+ Send,T:Send,E:Display + Debug + Send,{// Proxy callback:let callback_proxy = move |dt:&DatabaseTransaction| {//dt lifetime:'1// lifetime may not live long enough// returning this value requires that `'1` must outlive `'2`// the parameter type `F` may not live long enough [E0310]// the parameter type `F` must be valid for the static lifetime..// todo: How to add an explicit lifetime boundBox::pin(async{// Do something :eg: log.info().await;callback(dt).await// callback lifetime :'2})asPin<Box<dynFuture<Output = Result<T,E>> + Send + '_>>};self.inner_conn.transaction(callback_proxy).await}}// simple function 1asyncfnproxy_callback1<F,T,E>(callback:F,) -> implfor<'r>FnOnce(&'rDatabaseTransaction,) -> Pin<Box<dynFuture<Output = Result<T,E>> + Send + 'r>>
+ SendwhereF:for<'c>FnOnce(&'cDatabaseTransaction,) -> Pin<Box<dynFuture<Output = Result<T,E>> + Send + 'c>>
+ Send,T:Send,E:Display + Debug + Send,{move |dt:&DatabaseTransaction| {// the parameter type `F` may not live long enough [E0310]// the parameter type `F` must be valid for the static lifetime..Box::pin(asyncmove{do_something().await;callback(dt).await})}}// simple function 2asyncfnproxy_callback2<'a,F,T,E>(callback:F,) -> implFnOnce(&'aDatabaseTransaction) -> Pin<Box<dynFuture<Output = Result<T,E>> + Send + 'a>>
+ Send
+ 'awhereF:FnOnce(&'aDatabaseTransaction) -> Pin<Box<dynFuture<Output = Result<T,E>> + Send + 'a>>
+ Send
+ 'a,T:Send,E:Display + Debug + Send,{move |dt:&DatabaseTransaction| {// pass , but not general enough type :Box::pin(asyncmove{do_something().await;callback(dt).await})}}asyncfndo_something(){println!("do_something");}fnmain(){}
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
-
I'm working on a database transaction proxy system and encountering lifetime issues when trying to create a wrapper around async callbacks. I have a
TransactionTraitthat defines a transaction method taking an async callback, and I'm trying to create a proxy that can wrap and enhance these callbacks.I have two attempts at creating a proxy callback function, but both have lifetime issues:
Attempt 1:
proxy_callback1Error: The compiler complains that type
Fmay not live long enough and must be valid for the'staticlifetime.Attempt 2:
proxy_callback2This version compiles but is too restrictive - it requires all lifetimes to be the same (
'a), which doesn't match the more flexible higher-ranked trait bounds (HRTB) used in the trait.Attempt 3: Objective: Strengthen this closure
The Core Issue
I want to extract the proxy callback logic into a reusable function, but I can't seem to express the correct lifetime relationships. The trait uses HRTB (
for<'c>) to indicate that the callback should work for any lifetime'cof theDatabaseTransaction, but when I try to create a standalone function, I either get lifetime errors or the signature becomes too restrictive.What I've Tried
+ 'staticboundsQuestions
All Code
Beta Was this translation helpful? Give feedback.
All reactions