GJoshi
GJoshi

Reputation: 171

accessing a struct level reference in closure

We have a struct where we are trying to store the redis connection pool

struct RedisAuth {
    #[allow(dead_code)]
    configuration: Conf
    redis_pool: r2d2_pool::R2D2Pool
    
}

Now we have to implement trait which has closure function (this is a template that we need to follow to provide proper implementation)

impl Plugin for RedisAuth {
    type Config = Conf;

    async fn new(init: PluginInit<Self::Config>) -> Result<Self, BoxError> {
        tracing::info!("{}", init.config.message);
        let redis_pool = r2d2_pool::connect().unwrap();       
        Ok(RedisAuth { configuration: init.config, redis_pool: redis_pool})
    }

    fn supergraph_service(
        &self,
        service: supergraph::BoxService,
    ) -> supergraph::BoxService {
        
        ServiceBuilder::new()
                    .checkpoint(|request : supergraph::Request|  {
                        let operation_name = &request.supergraph_request.body().operation_name;
                        let pool = self.redis_pool;
                        // Do some async call here to auth, and decide if to continue or not.
                        Ok(ControlFlow::Continue(request))
                    })
                    .buffered()
                    .service(service)
                    .boxed()
    }
}

In the checkpoint function when we are accessing the pool from self reference we are getting this error .

borrowed data escapes outside of associated function self escapes the associated function body here.

We do not want to initialize the pool again and again and want to use that inside the closure . Is there any workaround or refectoring that can be done to mitigate the solution . I cannot change the closure signature .

Upvotes: 1

Views: 507

Answers (1)

Aleksander Krauze
Aleksander Krauze

Reputation: 6120

Closures capture by reference by default. It means that this closure has a reference to self (in your pool variable). You then return that closure from the function. Here rust has problem, as you try to return something that holds a lifetime of &self but function signature prevents that. So you must move ownership of pool into your closure.

Now I see that simply adding move keyword (which will make capture variables by move) will not be enough, as you would then move out of self, which is another problem. Try wrapping redis_pool in some kind of a shared reference, like Rc or Arc. And then simply clone this reference and pass ownership of it to the closure.

use std::sync::Arc;

struct RedisAuth {
    #[allow(dead_code)]
    configuration: Conf
    redis_pool: Arc<r2d2_pool::R2D2Pool>
    
}

impl Plugin for RedisAuth {
    type Config = Conf;

    async fn new(init: PluginInit<Self::Config>) -> Result<Self, BoxError> {
        tracing::info!("{}", init.config.message);
        let redis_pool = r2d2_pool::connect().unwrap();
        let redis_pool = Arc::new(redis_pool)
        Ok(RedisAuth { configuration: init.config, redis_pool: redis_pool})
    }

    fn supergraph_service(
        &self,
        service: supergraph::BoxService,
    ) -> supergraph::BoxService {
        
        let pool = Arc::clone(&self.redis_pool);
        ServiceBuilder::new()
                    .checkpoint(move |request : supergraph::Request|  {
                        let operation_name = &request.supergraph_request.body().operation_name;
                        // now you can use pool here
                        // Do some async call here to auth, and decide if to continue or not.
                        Ok(ControlFlow::Continue(request))
                    })
                    .buffered()
                    .service(service)
                    .boxed()
    }
}

EDIT. Added example usage of Arc.

Upvotes: 2

Related Questions