Skip to main content

Vc

Struct Vc 

Source
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 bincode crate.

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).

A diagram showing where cells are stored in tasks
TaskOutputs point to a specific task, and refer to the output cell for that task. TaskCells point to a specific cell within a task. Task cells are stored in a table (conceptually a Map<ValueTypeId, Vec<SharedReference>>) and are referenced by a pair of a type id and a sequentially allocated index.

§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, or FrozenMap in place of types like HashMap (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 Vc can be invalidated by changing dependencies or cache evicted, meaning that awaiting a Vc multiple times can give different results. A ReadRef is snapshot of the underlying cell at a point in time.

  • Reading (awaiting) Vcs causes the current task to be tracked a dependent of the Vc’s task or task cell. When the read task or task cell changes, the current task may be re-executed.

  • Vc types are always Copy. Most Futures are not. This works because Vcs are represented as a few ids or indices into data structures managed by the turbo-tasks framework. Vc types are not reference counted, but do support tracing for a hypothetical (unimplemented) garbage collector.

  • An uncached turbo_tasks::function that returns a Vc begins after being called, even if the Vc is not awaited.

§Subtypes

There are a couple of explicit “subtypes” of Vc. These can both be cheaply converted back into a Vc.

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.

RepresentationEqualityDowncastingStrong ConsistencyCollectiblesNon-Local
VcOne of manyBroken⚠️ After resolution❌ Eventual❌ NoNo
ResolvedVcTask Id + Type Id + Cell Id✅ Yes*Yes, cheaply❌ Eventual❌ No✅ Yes
OperationVcTask Id✅ Yes*⚠️ After resolutionSupportedYes✅ 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, Inner> Vc<T>
where T: VcValueType<Read = VcTransparentRead<T, Inner>>, Inner: Any + Send + Sync,

Source

pub fn cell(inner: Inner) -> Self

Source§

impl<T> Vc<T>
where T: ?Sized,

Source

pub async fn debug_identifier(vc: Self) -> Result<String>

Returns a debug identifier for this Vc.

Source

pub fn into_raw(vc: Self) -> RawVc

Returns the RawVc corresponding to this Vc.

Source

pub fn upcast<K>(vc: Self) -> Vc<K>
where T: UpcastStrict<K>, K: VcValueTrait + ?Sized,

Upcasts the given Vc<T> to a Vc<Box<dyn K>>.

This is also available as an Into/From conversion.

Source

pub fn upcast_non_strict<K>(vc: Self) -> Vc<K>
where T: Upcast<K>, K: VcValueTrait + ?Sized,

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.

Source

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.

Source

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.

Source

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.

Source

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.

Source

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,

Source

pub fn strongly_consistent(self) -> ReadVcFuture<T>

Do not use this: Use OperationVc::read_strongly_consistent instead.

Source

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.

Source

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>
where T: VcValueType, <<T as VcValueType>::Read as VcRead<T>>::Target: Clone,

Source

pub fn owned(self) -> ReadOwnedVcFuture<T>

Read the value and returns a owned version of it. It might clone the value.

Source§

impl<T> Vc<T>
where T: VcValueType, <<T as VcValueType>::Read as VcRead<T>>::Target: KeyedEq,

Source

pub fn get<'l, Q>(self, key: &'l Q) -> ReadKeyedVcFuture<'l, T, Q>
where Q: Hash + ?Sized, <<T as VcValueType>::Read as VcRead<T>>::Target: KeyedAccess<Q>,

Read the value and selects a keyed value from it. Only depends on the used key instead of the full value.

Source

pub fn contains_key<'l, Q>( self, key: &'l Q, ) -> ReadContainsKeyedVcFuture<'l, T, Q>
where Q: Hash + ?Sized, <<T as VcValueType>::Read as VcRead<T>>::Target: KeyedAccess<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,

Source

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,

Source§

fn borrow_decode<__D: BorrowDecoder<'__de, Context = __Context>>( decoder: &mut __D, ) -> Result<Self, DecodeError>

Attempt to decode this type with the given BorrowDecode.
Source§

impl<T> Clone for Vc<T>
where T: ?Sized,

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
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.

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§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T, __Context> Decode<__Context> for Vc<T>
where T: ?Sized,

Source§

fn decode<__D: Decoder<Context = __Context>>( decoder: &mut __D, ) -> Result<Self, DecodeError>

Attempt to decode this type with the given Decode.
Source§

impl<T> Default for Vc<T>
where T: ValueDefault,

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

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>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl<T> Encode for Vc<T>
where T: ?Sized,

Source§

fn encode<__E: Encoder>(&self, encoder: &mut __E) -> Result<(), EncodeError>

Encode a given type.
Source§

impl<T> From<RawVc> for Vc<T>
where T: ?Sized,

Source§

fn from(node: RawVc) -> Self

Converts to this type from the input type.
Source§

impl<T> Hash for Vc<T>
where T: ?Sized,

Source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<T> IntoFuture for &Vc<T>
where T: VcValueType,

Source§

type Output = <ReadVcFuture<T> as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = ReadVcFuture<T>

Which kind of future are we turning this into?
Source§

fn into_future(self) -> Self::IntoFuture

Creates a future from a value. Read more
Source§

impl<T> IntoFuture for &mut Vc<T>
where T: VcValueType,

Source§

type Output = <ReadVcFuture<T> as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = ReadVcFuture<T>

Which kind of future are we turning this into?
Source§

fn into_future(self) -> Self::IntoFuture

Creates a future from a value. Read more
Source§

impl<T> IntoFuture for Vc<T>
where T: VcValueType,

Source§

type Output = <ReadVcFuture<T> as Future>::Output

The output that the future will produce on completion.
Source§

type IntoFuture = ReadVcFuture<T>

Which kind of future are we turning this into?
Source§

fn into_future(self) -> Self::IntoFuture

Creates a future from a value. Read more
Source§

impl<T> PartialEq for Vc<T>
where T: ?Sized,

Source§

fn eq(&self, other: &Self) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl<T> Serialize for Vc<T>
where T: ?Sized,

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl<T> TaskInput for Vc<T>
where T: Send + Sync + ?Sized,

Source§

fn is_resolved(&self) -> bool

This should return true if there are any unresolved Vcs in the type. Read more
Source§

fn is_transient(&self) -> bool

This should return true if this object contains a Vc (or any subtype of Vc) pointing to a cell owned by a transient task. Read more
Source§

async fn resolve_input(&self) -> Result<Self>

This method should resolve any 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>
where T: Send + ?Sized,

Source§

impl<T> TraceRawVcs for Vc<T>
where T: ?Sized,

Source§

fn trace_raw_vcs(&self, trace_context: &mut TraceRawVcsContext)

Source§

fn get_raw_vcs(&self) -> Vec<RawVc>

Source§

impl<T> ValueDebugFormat for Vc<T>
where T: UpcastStrict<Box<dyn ValueDebug>> + Send + Sync + ?Sized,

Available on debug-assertions enabled only.
Source§

impl<T> Copy for Vc<T>
where T: ?Sized,

Source§

impl<T> Eq for Vc<T>
where T: ?Sized,

Source§

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>
where T: Send + ?Sized,

§

impl<T> Sync for Vc<T>
where T: Sync + ?Sized,

§

impl<T> UnsafeUnpin for Vc<T>
where T: ?Sized,

§

impl<T> UnwindSafe for Vc<T>
where T: UnwindSafe + ?Sized,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
§

impl<T> ArchivePointee for T

§

type ArchivedMetadata = ()

The archived version of the pointer metadata for this type.
§

fn pointer_metadata( _: &<T as ArchivePointee>::ArchivedMetadata, ) -> <T as Pointee>::Metadata

Converts some archived metadata to the pointer metadata for itself.
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
§

impl<T> DynHash for T
where T: Any + Hash,

§

fn dyn_hash(&self, state: &mut dyn Hasher)

§

impl<T> DynPartialEq for T
where T: Any + PartialEq,

§

fn dyn_partial_eq(&self, other: &(dyn Any + 'static)) -> bool

Source§

impl<T> DynTaskInputs for T
where T: Debug + Eq + Hash + Send + Sync + TraceRawVcs + 'static,

Source§

fn dyn_type_name(&self) -> &'static str

§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts 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 more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts 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
§

impl<T> LayoutRaw for T

§

fn layout_raw(_: <T as Pointee>::Metadata) -> Result<Layout, LayoutError>

Returns the layout of the type.
§

impl<T, N1, N2> Niching<NichedOption<T, N1>> for N2
where T: SharedNiching<N1, N2>, N1: Niching<T>, N2: Niching<T>,

§

unsafe fn is_niched(niched: *const NichedOption<T, N1>) -> bool

Returns whether the given value has been niched. Read more
§

fn resolve_niched(out: Place<NichedOption<T, N1>>)

Writes data to out indicating that a T is niched.
§

impl<T> Pointee for T

§

type Metadata = ()

The metadata type for pointers and references to this type.
Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

§

impl<T> DynEq for T
where T: Any + Eq,