Skip to main content

tracked

Attribute Macro tracked 

Source
#[tracked]
Expand description

Defines a tracked struct or function, or enables tracked methods in an impl block.

The accepted syntax and generated API depend on the annotated item. See the sections below for the options and field attributes accepted by each form.

§Tracked structs

A tracked struct represents a derived entity created during tracked-function execution. Its identity belongs to the producing query, which can recreate and update the entity in a later revision.

The annotated item must have named fields and exactly one lifetime parameter, conventionally 'db; type and const parameters are not supported. A field whose type is unconditionally 'static is accepted directly; any other field must implement salsa::SalsaValue.

See tracked structs in the salsa crate documentation for their identity, change tracking, and lifecycle.

§Struct options

  • constructor = IDENT renames the generated constructor from new to IDENT.
  • debug implements Debug using the field values when a database is attached to the current thread. The generated default_debug_fmt method can also be called from a manual Debug implementation.
  • heap_size = PATH records heap use for Salsa’s unstable memory-usage reporting. PATH must accept a reference to the tuple of all fields and return its heap allocation size in bytes.
  • persist enables persistent caching when Salsa’s persistence feature is enabled. Fields are serialized as a tuple with serde by default.
  • persist(serialize = PATH, deserialize = PATH) enables persistence with custom tuple serialization functions. Either path may be omitted to use the corresponding serde implementation.

§Struct field attributes

  • #[tracked] excludes the field from the struct’s identity. When the producing query recreates the same entity with a new value for this field, Salsa updates the existing entity instead of creating a new one. Reads of the field are tracked separately, so changing it invalidates only queries that read that field. Use this for properties that may change while the conceptual entity remains the same.
  • #[returns(MODE)] selects how the getter returns the field. ref (the default) returns &FieldTy; clone returns an owned FieldTy using [Clone]; copy returns an owned FieldTy using [Copy]; and deref uses Deref to return &<FieldTy as Deref>::Target. as_ref and as_deref use salsa::SalsaAsRef and salsa::SalsaAsDeref to return borrowed forms such as Option<&T> and Option<&T::Target>. Every borrowed result is tied to the database borrow.
  • #[get(IDENT)] renames the generated getter.
  • #[no_eq] replaces the stored value and treats the field as changed whenever the struct is recreated, avoiding the [PartialEq] requirement. It is most useful together with #[tracked]: because the field does not contribute to identity, the struct can retain its identity when recreated, while readers of the field are always invalidated.
  • Unsafe: #[salsa_value(unsafe(prove_safe_to_retain_manually))] suppresses the retention check for this field. The caller must ensure Salsa can retain the field and expose it with a later database lifetime.

Other attributes, including documentation and lint attributes, are copied to the generated getter.

§Tracked functions

A tracked function memoizes its result and records the Salsa values read by its body. Salsa reuses the memoized result while those dependencies remain unchanged.

The first parameter must be an immutable &dyn DatabaseTrait; the remaining parameters form the query key. The function may declare one database lifetime but no type or const parameters. Every key parameter and the output must implement [Send] + [Sync]. With no key parameters, the function has one memoized query per database. A single key parameter must be a Salsa struct and uses its ID directly. With multiple key parameters, Salsa first interns their tuple to obtain an ID, adding an interning step to every call. Each key parameter must additionally implement [Clone] + Eq + Hash. Equality and hashing determine whether calls use the same memo, and Salsa always clones the stored tuple when materializing the function arguments. Interned key parameters and outputs whose types are not unconditionally 'static must implement salsa::SalsaValue.

See tracked functions in the salsa crate documentation for query identity, dependency tracking, result equality, and memo lifecycle.

§Function options

  • returns(MODE) selects how callers receive the memoized result. ref (the default) returns &Output; clone returns an owned Output using [Clone]; copy returns an owned Output using [Copy]; and deref uses Deref to return &<Output as Deref>::Target. as_ref and as_deref use salsa::SalsaAsRef and salsa::SalsaAsDeref to return borrowed forms such as Option<&T> and Option<&T::Target>. Every borrowed result is tied to the database borrow and remains stored in the query’s memo.
  • no_eq treats every newly computed result as changed and removes the output’s equality requirement. It cannot be combined with cycle_fn.
  • specify generates FUNCTION::specify(db, key, value). It supports queries that have both a per-key incremental implementation and a batch implementation that computes many results at once. The function must take exactly one key argument, and it must be a tracked struct, not an input or interned struct. specify must be called during the same tracked query invocation that created the key. It cannot be combined with lru. See specifying query results in the Salsa book for an example.
  • lru = INTEGER bounds the number of memoized values retained by the function and sets the initial capacity used by FUNCTION::set_lru_capacity.
  • cycle_initial = EXPR enables fixed-point cycle recovery and computes the initial value. The expression is called as (db, cycle_head_id, query_arguments...).
  • cycle_fn = EXPR combines successive fixed-point values. It must be accompanied by cycle_initial and is called as (db, cycle, previous_value, new_value, query_arguments...). See fixed-point cycle recovery in the Salsa book for the convergence requirements and a complete example.
  • cycle_result = EXPR supplies an immediate fallback for cycles instead of fixed-point iteration. It is called with the same arguments as cycle_initial and cannot be combined with cycle_initial or cycle_fn.
  • heap_size = PATH records heap use for Salsa’s unstable memory-usage reporting. PATH must accept a reference to the output and return its heap allocation size in bytes.
  • persist enables persistent caching when Salsa’s persistence feature is enabled. The query inputs and output must implement serde::Serialize and serde::Deserialize.
  • self_ty = TYPE prefixes the query’s debug name with TYPE. The impl-block form supplies this automatically for methods and associated functions.

§Legacy function adapter

  • Unsafe: unsafe(non_salsa_values) is strongly discouraged. It adapts output or internally interned input types that do not implement salsa::SalsaValue by suppressing the generated checks. The caller becomes responsible for ensuring retained values remain valid across revisions. Prefer deriving or implementing salsa::SalsaValue for those types.

§Tracked impl blocks

Applying #[salsa::tracked] to an inherent or trait impl allows individual methods and associated functions in it to also use #[salsa::tracked(...)]. The outer attribute accepts no options; inner attributes accept all tracked-function options.

A tracked method takes self by value followed by the database parameter. A tracked associated function takes the database parameter first. Other methods and associated items are left unchanged.

§Examples

ⓘ
#[salsa::input]
struct File {
    #[returns(deref)]
    text: String,
}

#[salsa::tracked(returns(copy))]
fn word_count(db: &dyn salsa::Database, file: File) -> usize {
    file.text(db).split_whitespace().count()
}

#[salsa::tracked]
impl File {
    #[salsa::tracked(returns(copy))]
    fn line_count(self, db: &dyn salsa::Database) -> usize {
        self.text(db).lines().count()
    }
}