State
Turbo-Engine requires all function to be pure and deterministic, but sometimes one need to manage some state during the execution. State is ok as long as functions that read the state are correctly invalidated when the state changes.
To help with that Turbo Engine comes with a State<T>
struct that automatically handles invalidation.
Usage
#[turbo_tasks::value]
struct SomeValue {
immutable_value: i32,
mutable_value: State<i32>,
}
#[turbo_tasks::value_impl]
impl SomeValue {
#[turbo_tasks::function]
fn new(immutable_value: i32, mutable_value: i32) -> Vc<SomeValue> {
Self {
immutable_value,
mutable_value: State::new(mutable_value),
}
.cell()
}
#[turbo_tasks::function]
fn get_mutable_value(&self) -> Vc<i32> {
// Reading the state will make the current task depend on the state
// Changing the state will invalidate `get_mutable_value`.
let value = self.mutable_value.get();
Vc::cell(value)
}
#[turbo_tasks::function]
fn method(&self) {
// Writing the state will invalidate all reader of the state
// But only if the state has been changed.
self.mutable_value.update_conditionally(|value: &mut i32| {
*old += 1;
// Return true when the value has been changed
true
});
// There are more ways to update the state:
// Sets a new value. Compared this value with the old value and only update if the value has changed.
// Requires `PartialEq` on the value type.
self.mutable_value.set(42);
// Sets a new value unconditionally. Always update the state and invalidate all readers.
// Doesn't require `PartialEq` on the value type.
self.mutable_value.set_unconditionally(42);
}
}
State and Persistent Caching
TODO (Not implemented yet)
State is persistent in the Persistent Cache.
Best Practice
Use State
only when necessary. Prefer pure functions and immutable values.