定义数据库结构体

现在我们已经定义了 Jar,接下来要创建数据库结构体。

数据库结构体是所有 Jars 聚集在一起的地方。通常,它只由应用程序的“驱动程序” (driver) 使用:启动程序、提供输入的程序和转发输出的程序。

calc 中,数据库结构体在 db 模块中,看起来是这样的:


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

    // The logs are only used for testing and demonstrating reuse:
    //
    logs: Option<Arc<Mutex<Vec<String>>>>,
}
}

#[salsa::db(...)] 属性把要包含的所有 Jars 作为参数。

该结构体必须有一个名为 storage 的字段,其类型为 salsa::Storage<Self>,但此外也可以包含你想要的任何其他字段。

storage 结构体拥有 db 属性中列出的 Jars 的所有数据。

#[salsa::db(...)] 属性为我们前面看到的 salsa::HasJar<crate::Jar> trait 自动生成一组实现。

实现 salsa::Database trait

除了结构体本身,我们还必须添加一个 salsa::Database 实现:


#![allow(unused)]
fn main() {
impl salsa::Database for Database {
    fn salsa_event(&self, event: salsa::Event) {
        // Log interesting events, if logging is enabled
        if let Some(logs) = &self.logs {
            // don't log boring events
            if let salsa::EventKind::WillExecute { .. } = event.kind {
                logs.lock()
                    .unwrap()
                    .push(format!("Event: {:?}", event.debug(self)));
            }
        }
    }
}
}

实现 salsa::ParallDatabase trait

如果你想要允许同时从多个线程访问数据库,那么你还需要实现 ParallDatabase trait:


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

实现 Default trait

这不是必需的,但实现 Default 通常是让使用者实例化你的数据库的一种便捷方式:


#![allow(unused)]

fn main() {
}

给每个 Jar 实现 traits

Database 结构体还需要让每个 Jar 实现数据库 trait。

不过,在我们的例子中,已经通过 blanket impl 为做到了,因此不需要执行任何操作。

这是推荐的做法,除非你的 trait 依赖于 Database 本身的字段的自定义成员(例如,有时 Database 包含你想要访问的某种自定义资源)。