pub struct Vc<T>where
T: ?Sized,{ /* private fields */ }
Expand description
A “Value Cell” (Vc
for short) is a reference to a memoized computation result stored on the
heap or in persistent cache, depending on the Turbo Engine backend implementation.
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();
Vc
s are similar to a Future
or a Promise with a few key differences:
-
The value pointed to by a
Vc
can be invalidated by changing dependencies or cache evicted, meaning thatawait
ing aVc
multiple times can give different results. AReadRef
is snapshot of the underlying cell at a point in time. -
Reading (
await
ing)Vc
s 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. -
Vc
types are alwaysCopy
. MostFuture
s are not. This works becauseVc
s are represented as a few ids or indicies into data structures managed by theturbo-tasks
framework.Vc
types are not reference counted, but do support tracing for a hypothetical (unimplemented) garbage collector. -
Unlike futures (but like promises), the work that a
Vc
represents begins execution even if theVc
is notawait
ed.
For a more in-depth explanation of the concepts behind value cells, refer to the Turbopack book.
§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::cell
orvalue_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. Exact type information of trait types (i.e.Vc<Box<dyn Trait>>
) is not known because the function may not have finished execution yet.OperationVc
s must first beconnect
ed 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.
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
§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.
Function calls are neither “eager”, nor “lazy”. Even if not awaited, they 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 due to dirty task invalidation.
§Equality & Hashing
Because Vc
s can be equivalent but have different representation, it’s not recommended to
compare Vc
s 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
.
§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 Vc
s 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, Repr> Vc<T>where
T: VcValueType<Read = VcTransparentRead<T, Inner, Repr>>,
Inner: Any + Send + Sync,
Repr: VcValueType,
impl<T, Inner, Repr> Vc<T>where
T: VcValueType<Read = VcTransparentRead<T, Inner, Repr>>,
Inner: Any + Send + Sync,
Repr: VcValueType,
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 async fn resolve(self) -> Result<Vc<T>>
pub async fn resolve(self) -> Result<Vc<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 async fn to_resolved(self) -> Result<ResolvedVc<T>>
pub async fn to_resolved(self) -> Result<ResolvedVc<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.
Sourcepub async fn resolve_strongly_consistent(self) -> Result<Self>
pub async fn resolve_strongly_consistent(self) -> Result<Self>
Do not use this: Use OperationVc::resolve_strongly_consistent
instead.
Source§impl<T> Vc<T>where
T: VcValueTrait + ?Sized,
impl<T> Vc<T>where
T: VcValueTrait + ?Sized,
Sourcepub async fn try_resolve_sidecast<K>(
vc: Self,
) -> Result<Option<Vc<K>>, ResolveTypeError>where
K: VcValueTrait + ?Sized,
pub async fn try_resolve_sidecast<K>(
vc: Self,
) -> Result<Option<Vc<K>>, ResolveTypeError>where
K: VcValueTrait + ?Sized,
Attempts to sidecast the given Vc<Box<dyn T>>
to a Vc<Box<dyn K>>
.
This operation also resolves the Vc
.
Returns None
if the underlying value type does not implement K
.
Note: if the trait T is required to implement K, use
Vc::upcast(vc).resolve()
instead. This provides stronger guarantees,
removing the need for a Result
return type.
Sourcepub async fn try_resolve_downcast<K>(
vc: Self,
) -> Result<Option<Vc<K>>, ResolveTypeError>
pub async fn try_resolve_downcast<K>( vc: Self, ) -> Result<Option<Vc<K>>, ResolveTypeError>
Attempts to downcast the given Vc<Box<dyn T>>
to a Vc<K>
, where K
is of the form Box<dyn L>
, and L
is a value trait.
This operation also resolves the Vc
.
Returns None
if the underlying value type is not a K
.
Sourcepub async fn try_resolve_downcast_type<K>(
vc: Self,
) -> Result<Option<Vc<K>>, ResolveTypeError>where
K: Upcast<T> + VcValueType,
pub async fn try_resolve_downcast_type<K>(
vc: Self,
) -> Result<Option<Vc<K>>, ResolveTypeError>where
K: Upcast<T> + VcValueType,
Attempts to downcast the given Vc<Box<dyn T>>
to a Vc<K>
, where K
is a value type.
This operation also resolves the Vc
.
Returns None
if the underlying value type is not a K
.
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.
Trait Implementations§
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> IntoTraitRef for Vc<T>where
T: VcValueTrait + ?Sized,
impl<T> IntoTraitRef for Vc<T>where
T: VcValueTrait + ?Sized,
type ValueTrait = T
type Future = ReadVcFuture<T, VcValueTraitCast<T>>
fn into_trait_ref(self) -> Self::Future
Source§impl<T> TaskInput for Vc<T>
impl<T> TaskInput for Vc<T>
fn is_resolved(&self) -> bool
fn is_transient(&self) -> bool
async fn resolve_input(&self) -> Result<Self>
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>
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> UnwindSafe for Vc<T>where
T: UnwindSafe + ?Sized,
Blanket Implementations§
§impl<T> Any for Twhere
T: Any,
impl<T> Any for Twhere
T: Any,
fn get_type_id(&self) -> TypeId
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,
Source§impl<T> DynamicEqHash for T
impl<T> DynamicEqHash for T
§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