Defining the database struct

Now that we have defined a jar, we need to create the database struct. The database struct is where all the jars come together. Typically it is only used by the "driver" of your application; the one which starts up the program, supplies the inputs, and relays the outputs.

In calc, the database struct is in the db module, and it looks like this:


#![allow(unused)]
fn main() {
#[salsa::db(crate::Jar)]
pub(crate) struct Database {
    storage: salsa::Storage<Self>,
}
}

The #[salsa::db(...)] attribute takes a list of all the jars to include. The struct must have a field named storage whose types is salsa::Storage<Self>, but it can also contain whatever other fields you want. The storage struct owns all the data for the jars listed in the db attribute.

The salsa::db attribute autogenerates a bunch of impls for things like the salsa::HasJar<crate::Jar> trait that we saw earlier. This means that

Implementing the salsa::Database trait

In addition to the struct itself, we must add an impl of salsa::Database:


#![allow(unused)]
fn main() {
impl salsa::Database for Database {
    fn salsa_runtime(&self) -> &salsa::Runtime {
        self.storage.runtime()
    }
}
}

Impementing the salsa::ParallelDatabase trait

If you want to permit accessing your database from multiple threads at once, then you also need to implement the ParallelDatabase trait:


#![allow(unused)]
fn main() {
impl salsa::ParallelDatabase for Database {
    fn snapshot(&self) -> salsa::Snapshot<Self> {
        salsa::Snapshot::new(Database {
            storage: self.storage.snapshot(),
        })
    }
}
}

Implementing the Default trait

It's not required, but implementing the Default trait is often a convenient way to let users instantiate your database:


#![allow(unused)]
fn main() {
impl Default for Database {
    fn default() -> Self {
        Self {
            storage: Default::default(),
        }
    }
}
}

Implementing the traits for each Jar

The Database struct also needs to implement the database traits for each jar. In our case, though, we already wrote that impl as a blanket impl alongside the jar itself, so no action is needed. This is the recommended strategy unless your trait has custom members that depend on fields of the Database itself (for example, sometimes the Database holds some kind of custom resource that you want to give access to).