1

I am connecting to a database. Once the credentials are inserted, the connector gives me back a (client, connection) tuple. Afterwards, a procedural macro must access some "global location", retrieves the credentials from there and wires the data into the generated code.

I tried something like this:

pub static RUNTIME_DATA: RuntimeData<'static> = RuntimeData::new().await;

struct RuntimeData<'a> {
    db_credentials: DatabaseConnection<'static>,
    phantom: PhantomData<&'a RuntimeData<'a>>
}

unsafe impl Send for RuntimeData<'_> {}
unsafe impl Sync for RuntimeData<'_> {}

impl<'a> RuntimeData<'a> {
    pub async fn new() -> RuntimeData<'a> {
        Self {
            db_credentials: DatabaseConnection::new().await.unwrap(),
            phantom: PhantomData,
        }
    }
}

I can't .await in static contexts. Is there a workaround to hold the data of RuntimeData on RUNTIME_DATA?

I am trying to avoid calling DatabaseConnection::new() on every proc-macro use as it parses the credentials from a config file and wires them into the instance. I see that Rust does this really fast, even if I think that it's quite inefficient. If I can store the credentials once in a static context, I can avoid allocating multiple instances for every proc-macro use.

My database handler is:

pub struct DatabaseConnection<'a> {
    pub client: Client,
    pub connection: Connection<Socket, NoTlsStream>,
    pub phantom: &'a PhantomData<DatabaseConnection<'a>>
}

unsafe impl Send for DatabaseConnection<'_> {}
unsafe impl Sync for DatabaseConnection<'_> {}

impl<'a> DatabaseConnection<'a> {

    pub async fn new() -> Result<DatabaseConnection<'a>, Error> {

        let credentials = DatabaseCredentials::new();

        let (new_client, new_connection) =
            tokio_postgres::connect(
            &format!(
                "postgres://{user}:{pswd}@localhost/{db}",
                    user = credentials.username, 
                    pswd = credentials.password, 
                    db = credentials.db_name
                )[..], 
            NoTls)
            .await?;

        Ok(Self {
            client: new_client,
            connection: new_connection,
            phantom: &PhantomData
        })
    }
}

Edit for clarifiyin PitaJ approach:

pub static lazy: Lazy<RuntimeData<'static>> = Lazy::new(|| async {
     block_on(RuntimeData::new()).await.unwrap()
});
Alex Vergara
  • 1,766
  • 1
  • 10
  • 29
  • 1
    You probably want to use something like [`futures::executor::block_on`](https://docs.rs/futures/latest/futures/executor/fn.block_on.html) inside a [`once_cell::sync::Lazy`](https://docs.rs/once_cell/1.9.0/once_cell/sync/struct.Lazy.html). You may want `once_cell::unsync::Lazy` instead if you don't need to share the same database connection between threads. – PitaJ Mar 01 '22 at 15:43
  • You may want to change it so you use a static `Lazy::DatabaseCredentials` instead, if that's really what you're worried about. – PitaJ Mar 01 '22 at 19:46
  • Can you please provide a minimal example with a binding to a static variable? – Alex Vergara Mar 02 '22 at 08:13
  • The documentation I linked has examples. What are you having trouble with? I don't have all of your code so there's not much more I can do. – PitaJ Mar 02 '22 at 14:17
  • I am facing the issue that I the return value of Lazy doesn't implement Sync – Alex Vergara Mar 02 '22 at 15:51
  • Are you trying `Lazy` or `Lazy`? And are you using `sync::Lazy` or `unsync::Lazy`? Edit your question to include what you've tried, and the error you got. – PitaJ Mar 02 '22 at 16:07
  • Not so useful the edit, I am still trying to clarify the last comment. I tried several approaches, but I mainly focused on RuntimeData. I also tried to make it as the return value of the constructor of RuntimeData. I think I just did not understand the point on Lazy, sorry – Alex Vergara Mar 02 '22 at 16:40
  • Don't use `async` or `await` with the `Lazy` at all, instead try just `Lazy::new(|| { block_on(RuntimeData::new()).unwrap() })`. – PitaJ Mar 02 '22 at 17:26

1 Answers1

0

As pointed out, solution it's to use the block_on(...) method.

pub static lazy: Lazy<RuntimeData<'static>> = Lazy::new(|| async {
     block_on(RuntimeData::new()).await.unwrap()
});
Alex Vergara
  • 1,766
  • 1
  • 10
  • 29