2018-10-02 09:50:38 +00:00
|
|
|
The `hello_world` example is intended to walk through the very basics
|
|
|
|
of a salsa setup. Here is a more detailed writeup.
|
|
|
|
|
2018-10-05 08:54:51 +00:00
|
|
|
### Step 1: Define the database trait
|
2018-10-02 09:50:38 +00:00
|
|
|
|
2018-10-05 08:54:51 +00:00
|
|
|
The **database** is the central struct that holds all the state for
|
|
|
|
your application. It has the current values of all your inputs, the
|
|
|
|
values of any memoized queries you have executed thus far, and
|
2018-10-02 09:50:38 +00:00
|
|
|
dependency information between them.
|
|
|
|
|
|
|
|
In your program, however, you rarely interact with the **actual**
|
2018-10-05 08:54:51 +00:00
|
|
|
database struct. Instead, you interact with database **traits** that
|
|
|
|
you define. These traits define the set of queries that you need for
|
|
|
|
any given piece of code. You define them using the
|
|
|
|
`salsa::query_prototype!` macro.
|
|
|
|
|
|
|
|
Here is a simple example of a database trait from the `hello_world`
|
|
|
|
example. It defines exactly two queries: `input_string` and
|
|
|
|
`length`. You see that the `query_prototype!` macro just lists out the
|
|
|
|
names of the queries as methods (e.g., `input_string()`) and also a
|
2018-10-02 09:50:38 +00:00
|
|
|
path to a type that will define the query (`InputString`). It doesn't
|
|
|
|
give many other details: those are specified in the query definition
|
|
|
|
that comes later.
|
|
|
|
|
|
|
|
```rust
|
|
|
|
salsa::query_prototype! {
|
2018-10-05 08:54:51 +00:00
|
|
|
trait HelloWorldDatabase: salsa::Database {
|
2018-10-02 09:50:38 +00:00
|
|
|
fn input_string() for InputString;
|
|
|
|
fn length() for Length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
### Step 2: Define the queries
|
|
|
|
|
|
|
|
The actual query definitions are made using the
|
|
|
|
`salsa::query_definition` macro. For an **input query**, such as
|
|
|
|
`input_string`, these resemble a variable definition:
|
|
|
|
|
|
|
|
```rust
|
|
|
|
salsa::query_definition! {
|
|
|
|
InputString: Map<(), Arc<String>>;
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Here, the `Map` is actually a keyword -- you have to write it. The
|
|
|
|
idea is that each query isn't defining a single value: they are always
|
|
|
|
a mapping from some **key** to some **value** -- in this case, though,
|
|
|
|
the type of the key is just the unit type `()` (so in a sense this
|
|
|
|
*is* a single value). The value type would be `Arc<String>`.
|
|
|
|
|
|
|
|
Note that both keys and values are cloned with relative frequency, so
|
|
|
|
it's a good idea to pick types that can be cheaply cloned. Also, for
|
|
|
|
the incremental system to work, keys and value types must not employ
|
|
|
|
"interior mutability" (no `Mutex` or `AtomicUsize` etc).
|
|
|
|
|
|
|
|
Next let's define the `length` query, which is a function query:
|
|
|
|
|
|
|
|
```rust
|
|
|
|
salsa::query_definition! {
|
2018-10-05 08:54:51 +00:00
|
|
|
Length(db: &impl HelloWorldDatabase, _key: ()) -> usize {
|
2018-10-02 09:50:38 +00:00
|
|
|
// Read the input string:
|
2018-10-05 08:54:51 +00:00
|
|
|
let input_string = db.input_string().get(());
|
2018-10-02 09:50:38 +00:00
|
|
|
|
|
|
|
// Return its length:
|
|
|
|
input_string.len()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
Like the `InputString` query, `Length` has a **key** and a **value**
|
|
|
|
-- but this time the type of the key is specified as the type of the
|
|
|
|
second argument (`_key`), and the type of the value is specified from
|
|
|
|
the return type (`usize`).
|
|
|
|
|
2018-10-05 08:54:51 +00:00
|
|
|
You can also see that functions take a first argument, the `db`, which
|
|
|
|
always has the form `&impl <SomeDatabaseTrait>`. This `db` value gives
|
|
|
|
access to all the other queries that are listed in the context trait
|
|
|
|
that you specify.
|
2018-10-02 09:50:38 +00:00
|
|
|
|
|
|
|
In the first line of the function we see how we invoke a query:
|
|
|
|
|
|
|
|
```rust
|
2018-10-05 08:54:51 +00:00
|
|
|
let input_string = db.input_string().get(());
|
2018-10-02 09:50:38 +00:00
|
|
|
```
|
|
|
|
|
2018-10-05 08:54:51 +00:00
|
|
|
When you invoke `db.input_string()`, what you get back is called
|
2018-10-02 09:50:38 +00:00
|
|
|
a `QueryTable` -- it offers a few methods that let you interact with
|
|
|
|
the query. The main method you will use though is `get(key)` which --
|
|
|
|
given a `key` -- computes and returns the up-to-date value. In the
|
|
|
|
case of an input query like `input_string`, this just returns whatever
|
|
|
|
value has been set by the user (if no value has been set yet, it
|
|
|
|
returns the `Default::default()` value; all query inputs must
|
|
|
|
implement `Default`).
|
|
|
|
|
2018-10-05 08:54:51 +00:00
|
|
|
### Step 3: Define the database struct that implements the database trait
|
2018-10-02 09:50:38 +00:00
|
|
|
|
2018-10-05 08:54:51 +00:00
|
|
|
The final step is to create the **database struct** which will
|
|
|
|
implement your database trait(s). This struct combines all the parts
|
|
|
|
of your system into one whole; it can also add custom state of your
|
|
|
|
own (such as an interner or configuration). In our simple example
|
2018-10-02 09:50:38 +00:00
|
|
|
though we won't do any of that. The only field that you **actually**
|
|
|
|
need is a reference to the **salsa runtime**; then you must also
|
2018-10-05 08:54:51 +00:00
|
|
|
implement the `salsa::Database` trait to tell salsa where to find this
|
2018-10-02 09:50:38 +00:00
|
|
|
runtime:
|
|
|
|
|
|
|
|
```rust
|
|
|
|
#[derive(Default)]
|
2018-10-05 08:54:51 +00:00
|
|
|
struct DatabaseStruct {
|
|
|
|
runtime: salsa::runtime::Runtime<DatabaseStruct>,
|
2018-10-02 09:50:38 +00:00
|
|
|
}
|
|
|
|
|
2018-10-05 08:54:51 +00:00
|
|
|
impl salsa::Database for DatabaseStruct {
|
|
|
|
fn salsa_runtime(&self) -> &salsa::runtime::Runtime<DatabaseStruct> {
|
2018-10-02 09:50:38 +00:00
|
|
|
&self.runtime
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2018-10-05 08:54:51 +00:00
|
|
|
Next, you must use the `database_storage!` to define the "storage
|
2018-10-02 09:50:38 +00:00
|
|
|
struct" for your type. This storage struct contains all the hashmaps
|
|
|
|
and other things that salsa uses to store the values for your
|
|
|
|
queries. You won't need to interact with it directly. To use the
|
|
|
|
macro, you basically list out all the traits and each of the queries
|
|
|
|
within those traits:
|
|
|
|
|
|
|
|
```rust
|
2018-10-05 08:54:51 +00:00
|
|
|
salsa::database_storage! {
|
|
|
|
struct DatabaseStorage for DatabaseStruct {
|
|
|
|
// ^^^^^^^^^^^^^^^ --------------
|
|
|
|
// name of the type the name of your context type
|
2018-10-02 09:50:38 +00:00
|
|
|
// we will make
|
2018-10-05 08:54:51 +00:00
|
|
|
impl HelloWorldDatabase {
|
2018-10-02 09:50:38 +00:00
|
|
|
fn input_string() for InputString;
|
|
|
|
fn length() for Length;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
2018-10-05 08:54:51 +00:00
|
|
|
The `database_storage` macro will also implement the
|
|
|
|
`HelloWorldDatabase` trait for your query context type.
|
2018-10-02 09:50:38 +00:00
|
|
|
|
2018-10-05 08:54:51 +00:00
|
|
|
Now that we've defined our database, we can start using it:
|
2018-10-02 09:50:38 +00:00
|
|
|
|
|
|
|
```rust
|
|
|
|
fn main() {
|
2018-10-05 08:54:51 +00:00
|
|
|
let db = DatabaseStruct::default();
|
2018-10-02 09:50:38 +00:00
|
|
|
|
2018-10-05 08:54:51 +00:00
|
|
|
println!("Initially, the length is {}.", db.length().get(()));
|
2018-10-02 09:50:38 +00:00
|
|
|
|
2018-10-05 08:54:51 +00:00
|
|
|
db.input_string().set((), Arc::new(format!("Hello, world")));
|
2018-10-02 09:50:38 +00:00
|
|
|
|
2018-10-05 08:54:51 +00:00
|
|
|
println!("Now, the length is {}.", db.length().get(()));
|
2018-10-02 09:50:38 +00:00
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
And if we run this code:
|
|
|
|
|
|
|
|
```bash
|
|
|
|
> cargo run --example hello_world
|
|
|
|
Compiling salsa v0.2.0 (/Users/nmatsakis/versioned/salsa)
|
|
|
|
Finished dev [unoptimized + debuginfo] target(s) in 0.94s
|
|
|
|
Running `target/debug/examples/hello_world`
|
|
|
|
Initially, the length is 0.
|
|
|
|
Now, the length is 12.
|
|
|
|
```
|
|
|
|
|
|
|
|
Amazing.
|
|
|
|
|