pub struct Vc<T>where
T: ?Sized,{ /* private fields */ }Expand description
Value cells represent the pending result of a computation, similar to a cell in a spreadsheet. When a Vc’s contents change, the change is propagated by invalidating dependent tasks.
In order to get a reference to the pointed value, you need to .await the Vc<T> to get a ReadRef<T>:
let some_vc: Vc<T>;
let some_ref: ReadRef<T> = some_vc.await?;
some_ref.some_method_on_t();The returned ReadRef<T> represents a [reference-counted][triomphe::Arc] snapshot of a cell’s value at a given point in time.
§Understanding Cells
A cell is a storage location for data associated with a task. Cells provide:
- Immutability: Once a value is stored in a cell, it becomes immutable until that task is re-executed.
- Recomputability: If invalidated or cache evicted, a cell’s contents can be re-computed by re-executing its associated task.
- Dependency Tracking: When a cell’s contents are read (with
.await), the reading task is marked as dependent on the cell. - Persistence: Cells owned by persisted tasks are serializable using the
bincodecrate.
Cells are stored in arrays associated with the task that constructed them. A Vc can either point to a specific cell (this is a “resolved” cell), or the return value of a function (this is an “unresolved” cell).
§Constructing a Cell
Most types using the #[turbo_tasks::value] macro are given a .cell() method. This method returns a Vc of the type.
Transparent wrapper types that use #[turbo_tasks::value(transparent)] cannot define methods on their wrapped type, so instead the Vc::cell function can be used to construct these types.
§Updating a Cell
Every time a task runs, its cells are re-constructed.
When .cell() or Vc::cell is called, the cell counter for the ValueTypeId is incremented, and the value is compared to the previous execution’s using PartialEq. If the value with that index differs, the cell is updated, and all dependent tasks are invalidated.
The compare-then-update behavior can be overridden to always update and invalidate using the cell = "new" argument.
Because cells are keyed by a combination of their type and construction order, task functions should have a deterministic execution order. A function with inconsistent ordering may result in wasted work by invalidating additional cells, though it will still give correct results:
- You should use types with deterministic behavior. If you plan to iterate over a collection, use
IndexMap,BTreeMap, orFrozenMapin place of types likeHashMap(which gives randomized iteration order). - If you perform work in parallel within a single turbo-task, be careful not to construct cells inside the parts of your function that are executed across multiple threads. That can lead to accidentally non-deterministic behavior. Instead, collect results in parallel, and construct cells in the main thread after sorting the results.
§Reading Vcs
Vcs implement IntoFuture and can be awaited, but there are few key differences compared to a normal Future:
-
The value pointed to by a
Vccan be invalidated by changing dependencies or cache evicted, meaning thatawaiting aVcmultiple times can give different results. AReadRefis snapshot of the underlying cell at a point in time. -
Reading (
awaiting)Vcs causes the current task to be tracked a dependent of theVc’s task or task cell. When the read task or task cell changes, the current task may be re-executed. -
Vctypes are alwaysCopy. MostFutures are not. This works becauseVcs are represented as a few ids or indices into data structures managed by theturbo-tasksframework.Vctypes are not reference counted, but do support tracing for a hypothetical (unimplemented) garbage collector. -
An uncached
turbo_tasks::functionthat returns aVcbegins after being called, even if theVcis notawaited.
§Subtypes
There are a couple of explicit “subtypes” of Vc. These can both be cheaply converted back into a Vc.
-
ResolvedVc: (akaRawVc::TaskCell) A reference to a cell constructed within a task, as part of aVc::cellorvalue_type.cell()constructor. As the cell has been constructed at least once, the concrete type of the cell is known (allowing downcasting). This is stored as a combination of a task id, a type id, and a cell id. -
OperationVc: (akaRawVc::TaskOutput) The synchronous return value of aturbo_tasks::function. Internally, this is stored using a task id.OperationVcs must first beconnected before being read.
ResolvedVc is almost always preferred over the more awkward OperationVc API, but OperationVc can be useful when dealing with collectibles, when you need to read the result of a function with strong consistency, or with State.
These many representations are stored internally using a type-erased RawVc. Type erasure reduces the monomorphization (and therefore binary size and compilation time) required to support Vc and its subtypes.
This means that Vc often uses the same in-memory representation as a ResolvedVc or an OperationVc, but it does not expose the same methods (e.g. downcasting) because the exact memory representation is not statically defined.
| Representation | Equality | Downcasting | Strong Consistency | Collectibles | Non-Local | |
|---|---|---|---|---|---|---|
Vc | One of many | ❌ Broken | ⚠️ After resolution | ❌ Eventual | ❌ No | ❌ No |
ResolvedVc | Task Id + Type Id + Cell Id | ✅ Yes* | ✅ Yes, cheaply | ❌ Eventual | ❌ No | ✅ Yes |
OperationVc | Task Id | ✅ Yes* | ⚠️ After resolution | ✅ Supported | ✅ Yes | ✅ Yes |
* see the type’s documentation for details
§Equality & Hashing
Because Vcs can be equivalent but have different representation, it’s not recommended to compare Vcs by equality. Instead, you should convert a Vc to an explicit subtype first (likely ResolvedVc). Future versions of Vc may not implement Eq, PartialEq, or Hash.
§Execution Model
While task functions are expected to be side-effect free, their execution behavior is still important for performance reasons, or to code using collectibles to represent issues or side-effects.
Even if not awaited, uncached function calls are guaranteed to execute (potentially emitting collectibles) before the root task finishes or before the completion of any strongly consistent read containing their call. However, the exact point when that execution begins is an implementation detail. Functions may execute more than once if one of their dependencies is invalidated.
§Eventual Consistency
Because turbo_tasks is eventually consistent, two adjacent .awaits of the same Vc<T> may return different values. If this happens, the task will eventually be invalidated and re-executed by a strongly consistent root task. Top-level tasks will panic if they attempt to perform an eventually consistent read of a Vc.
Tasks affected by a read inconsistency can return errors. These errors will be discarded by the strongly consistent root task. Tasks should never panic due to a potentially-inconsistent value stored in a Vc.
Currently, all inconsistent tasks are polled to completion. Future versions of the turbo_tasks library may drop tasks that have been identified as inconsistent after some time. As non-root tasks should not perform side-effects, this should be safe, though it may introduce some issues with cross-process resource management.
§Optimization: Local Outputs
In addition to the potentially-explicit “resolved” and “operation” representations of a Vc, there’s another internal representation of a Vc, known as a “Local Vc”, or RawVc::LocalOutput.
This is a special case of the synchronous return value of a turbo_tasks::function when some of its arguments have not yet been resolved. These are stored in task-local state that is freed after their parent non-local task exits.
We prevent potentially-local Vcs from escaping the lifetime of a function using the NonLocalValue marker trait alongside some fallback runtime checks. We do this to avoid some ergonomic challenges that would come from using lifetime annotations with Vc.
Implementations§
Source§impl<T> Vc<T>where
T: ?Sized,
impl<T> Vc<T>where
T: ?Sized,
Sourcepub async fn debug_identifier(vc: Self) -> Result<String>
pub async fn debug_identifier(vc: Self) -> Result<String>
Returns a debug identifier for this Vc.
Sourcepub fn upcast<K>(vc: Self) -> Vc<K>
pub fn upcast<K>(vc: Self) -> Vc<K>
Upcasts the given Vc<T> to a Vc<Box<dyn K>>.
This is also available as an Into/From conversion.
Sourcepub fn upcast_non_strict<K>(vc: Self) -> Vc<K>
pub fn upcast_non_strict<K>(vc: Self) -> Vc<K>
Upcasts the given Vc<T> to a Vc<Box<dyn K>>
This has a loose type constraint which would allow upcasting to the same type, prefer using
Vc::upcast when possible. This is useful for extension traits and other more generic
usecases.
§Example
// In generic code where T might be the same as K
fn process_foo(vc: ResolvedVc<impl Upcast<Box<dyn MyTrait>>>) -> Vc<Foo> {
let my_trait: ResolvedVc<Box<dyn MyTrait>> = Vc::upcast_non_strict(vc);
my_trait.do_something()
}Using generics you could allow users to pass any compatible type, but if you specified
UpcastStrict<...> instead of Upcast<...> you would disallow calling this function if you
already had a ResolvedVc<Box<dyn MyTrait>>. So this function has a looser type constraint
to make these functions easier to write and use.
Sourcepub async fn as_side_effect(self) -> Result<()>
pub async fn as_side_effect(self) -> Result<()>
Runs the operation, but ignores the returned Vc. Use that when only interested in running the task for side effects.
Sourcepub fn resolve(self) -> ResolveVcFuture<T> ⓘ
pub fn resolve(self) -> ResolveVcFuture<T> ⓘ
Do not use this: Use Vc::to_resolved instead. If you must have a resolved Vc type
and not a ResolvedVc type, simply deref the result of Vc::to_resolved.
Sourcepub fn to_resolved(self) -> ToResolvedVcFuture<T> ⓘ
pub fn to_resolved(self) -> ToResolvedVcFuture<T> ⓘ
Resolve the reference until it points to a cell directly, and wrap the
result in a ResolvedVc, which statically guarantees that the
Vc was resolved.
Sourcepub fn is_resolved(self) -> bool
pub fn is_resolved(self) -> bool
Returns true if the reference is resolved, meaning the underlying RawVc uses the
RawVc::TaskCell representation.
If you need resolved Vc value, it’s typically better to use the ResolvedVc type to
enforce your requirements statically instead of dynamically at runtime.
See also ResolvedVc::to_resolved and RawVc::is_resolved.
Sourcepub fn is_local(self) -> bool
pub fn is_local(self) -> bool
Returns true if the Vc was by a local function call (e.g. one who’s arguments were not
fully resolved) and has not yet been resolved.
Aside from differences in caching, a function’s behavior should not be changed by using local or non-local cells, so this function is mostly useful inside tests and internally in turbo-tasks.
Source§impl<T> Vc<T>where
T: VcValueType,
impl<T> Vc<T>where
T: VcValueType,
Sourcepub fn strongly_consistent(self) -> ReadVcFuture<T> ⓘ
pub fn strongly_consistent(self) -> ReadVcFuture<T> ⓘ
Do not use this: Use OperationVc::read_strongly_consistent instead.
Sourcepub fn untracked(self) -> ReadVcFuture<T> ⓘ
pub fn untracked(self) -> ReadVcFuture<T> ⓘ
Returns a untracked read of the value. This will not invalidate the current function when the read value changed.
Sourcepub fn final_read_hint(self) -> ReadVcFuture<T> ⓘ
pub fn final_read_hint(self) -> ReadVcFuture<T> ⓘ
Read the value with the hint that this is the final read of the value. This might drop the cell content. Future reads might need to recompute the value.
Source§impl<T> Vc<T>
impl<T> Vc<T>
Sourcepub fn get<'l, Q>(self, key: &'l Q) -> ReadKeyedVcFuture<'l, T, Q>
pub fn get<'l, Q>(self, key: &'l Q) -> ReadKeyedVcFuture<'l, T, Q>
Read the value and selects a keyed value from it. Only depends on the used key instead of the full value.
Sourcepub fn contains_key<'l, Q>(
self,
key: &'l Q,
) -> ReadContainsKeyedVcFuture<'l, T, Q>
pub fn contains_key<'l, Q>( self, key: &'l Q, ) -> ReadContainsKeyedVcFuture<'l, T, Q>
Read the value and checks if it contains the given key. Only depends on the used key instead of the full value.
Source§impl<T> Vc<T>where
T: VcValueTrait + ?Sized,
impl<T> Vc<T>where
T: VcValueTrait + ?Sized,
Sourcepub fn into_trait_ref(self) -> ReadVcFuture<T, VcValueTraitCast<T>> ⓘ
pub fn into_trait_ref(self) -> ReadVcFuture<T, VcValueTraitCast<T>> ⓘ
Converts this trait vc into a trait reference.
The signature is similar to IntoFuture::into_future, but we don’t want trait vcs to
have the same future-like semantics as value vcs when it comes to producing refs. This
behavior is rarely needed, so in most cases, .awaiting a trait vc is a mistake.
Trait Implementations§
Source§impl<'__de, T, __Context> BorrowDecode<'__de, __Context> for Vc<T>where
T: ?Sized,
impl<'__de, T, __Context> BorrowDecode<'__de, __Context> for Vc<T>where
T: ?Sized,
Source§fn borrow_decode<__D: BorrowDecoder<'__de, Context = __Context>>(
decoder: &mut __D,
) -> Result<Self, DecodeError>
fn borrow_decode<__D: BorrowDecoder<'__de, Context = __Context>>( decoder: &mut __D, ) -> Result<Self, DecodeError>
Source§impl<T> Debug for Vc<T>where
T: ?Sized,
Generates an opaque debug representation of the Vc itself, but not the data inside of it.
impl<T> Debug for Vc<T>where
T: ?Sized,
Generates an opaque debug representation of the Vc itself, but not the data inside of it.
This is implemented to allow types containing Vc to implement the synchronous Debug
trait, but in most cases users should use the ValueDebug implementation to get a string
representation of the contents of the cell.
Source§impl<T> Default for Vc<T>where
T: ValueDefault,
impl<T> Default for Vc<T>where
T: ValueDefault,
Source§impl<'de, T> Deserialize<'de> for Vc<T>where
T: ?Sized,
impl<'de, T> Deserialize<'de> for Vc<T>where
T: ?Sized,
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
Source§impl<T> IntoFuture for &Vc<T>where
T: VcValueType,
impl<T> IntoFuture for &Vc<T>where
T: VcValueType,
Source§type Output = <ReadVcFuture<T> as Future>::Output
type Output = <ReadVcFuture<T> as Future>::Output
Source§type IntoFuture = ReadVcFuture<T>
type IntoFuture = ReadVcFuture<T>
Source§fn into_future(self) -> Self::IntoFuture
fn into_future(self) -> Self::IntoFuture
Source§impl<T> IntoFuture for &mut Vc<T>where
T: VcValueType,
impl<T> IntoFuture for &mut Vc<T>where
T: VcValueType,
Source§type Output = <ReadVcFuture<T> as Future>::Output
type Output = <ReadVcFuture<T> as Future>::Output
Source§type IntoFuture = ReadVcFuture<T>
type IntoFuture = ReadVcFuture<T>
Source§fn into_future(self) -> Self::IntoFuture
fn into_future(self) -> Self::IntoFuture
Source§impl<T> IntoFuture for Vc<T>where
T: VcValueType,
impl<T> IntoFuture for Vc<T>where
T: VcValueType,
Source§type Output = <ReadVcFuture<T> as Future>::Output
type Output = <ReadVcFuture<T> as Future>::Output
Source§type IntoFuture = ReadVcFuture<T>
type IntoFuture = ReadVcFuture<T>
Source§fn into_future(self) -> Self::IntoFuture
fn into_future(self) -> Self::IntoFuture
Source§impl<T> TaskInput for Vc<T>
impl<T> TaskInput for Vc<T>
Source§fn is_resolved(&self) -> bool
fn is_resolved(&self) -> bool
Source§fn is_transient(&self) -> bool
fn is_transient(&self) -> bool
Source§async fn resolve_input(&self) -> Result<Self>
async fn resolve_input(&self) -> Result<Self>
Vcs nested inside of this object, cloning the object in
the process. If the input is unresolved (TaskInput::is_resolved) a “local” resolution
task is created that runs this method.Source§impl<T> TaskOutput for Vc<T>
impl<T> TaskOutput for Vc<T>
Source§impl<T> TraceRawVcs for Vc<T>where
T: ?Sized,
impl<T> TraceRawVcs for Vc<T>where
T: ?Sized,
fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext)
fn get_raw_vcs(&self) -> Vec<RawVc>
Source§impl<T> ValueDebugFormat for Vc<T>
Available on debug-assertions enabled only.
impl<T> ValueDebugFormat for Vc<T>
fn value_debug_format(&self, depth: usize) -> ValueDebugFormatString<'_>
impl<T> Copy for Vc<T>where
T: ?Sized,
impl<T> Eq for Vc<T>where
T: ?Sized,
impl<T> Unpin for Vc<T>where
T: ?Sized,
Auto Trait Implementations§
impl<T> Freeze for Vc<T>where
T: ?Sized,
impl<T> RefUnwindSafe for Vc<T>where
T: RefUnwindSafe + ?Sized,
impl<T> Send for Vc<T>
impl<T> Sync for Vc<T>
impl<T> UnsafeUnpin for Vc<T>where
T: ?Sized,
impl<T> UnwindSafe for Vc<T>where
T: UnwindSafe + ?Sized,
Blanket Implementations§
§impl<T> ArchivePointee for T
impl<T> ArchivePointee for T
§type ArchivedMetadata = ()
type ArchivedMetadata = ()
§fn pointer_metadata(
_: &<T as ArchivePointee>::ArchivedMetadata,
) -> <T as Pointee>::Metadata
fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
§impl<T> DynPartialEq for T
impl<T> DynPartialEq for T
fn dyn_partial_eq(&self, other: &(dyn Any + 'static)) -> bool
Source§impl<T> DynTaskInputs for T
impl<T> DynTaskInputs for T
fn dyn_type_name(&self) -> &'static str
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.§impl<T> Instrument for T
impl<T> Instrument for T
§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left is true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
self into a Left variant of Either<Self, Self>
if into_left(&self) returns true.
Converts self into a Right variant of Either<Self, Self>
otherwise. Read more