#[repr(C)]pub struct TraitObjectStorage {
pub header: HeapHeader,
pub value: *const TypedObjectStorage,
pub vtable: Arc<VTable>,
}Expand description
dyn Trait storage — the typed-Arc replacement for the
bulldozer-deleted HeapValue::TraitObject { value: Box<u64>, vtable: Arc<VTable> }. Pairs the boxed data half (always a
TypedObject per §Q25.C.4 universal-dyn ruling — scalars/strings
that implement traits are boxed into TypedObject first; the
auto-boxing rule lifts Rust’s object-safety restrictions at the
cost of one heap indirection per dyn coerce) with the vtable
half (shared Arc<VTable> so per-impl vtables are constructed
once and IC-cached per §Q25.C.6).
Forbidden alternative. Box<u64> data half is explicitly
refused (ADR-006 §Q25.E #3 — kind-blind raw-bits storage, same
defection-attractor as the deleted ValueWord). The data half is
kinded by being a typed object with a schema — Arc<TypedObjectStorage>
recovers the per-field kind table via the schema_id.
Identity contract. Arc::ptr_eq on the vtable Arc is the
canonical equality for the §Q25.C.2 Self-arg runtime check;
vtable.concrete_type_id is the IC-stabilization key per §Q25.C.6.
Mirror of the §2.7.20 / §2.7.25 typed-Arc shape — refcount
discipline goes through the kind label (HeapKind::TraitObject = 29)
in clone_with_kind / drop_with_kind, NOT through HeapValue.
Method-receiver classification flows through slot.as_heap_value()
→ HeapValue::TraitObject(arc) per ADR-005 §1 single-discriminator;
the op_dyn_method_call opcode handler (compiler-emission tier)
uses the recovered Arc<TraitObjectStorage> to look up the method
in vtable.methods and dispatch the appropriate VTableEntry.
Wave 2 Agent E (2026-05-14): HeapHeader-equipped shape change.
Per audit §4.3 Obstacle O-3.a resolution + ADR-006 §Q25.C.5 amendment,
the struct now carries a HeapHeader at offset 0 (#[repr(C)]) so
v2-raw raw-pointer allocations (_new / _drop + impl HeapElement)
can dispatch refcount on the header via v2_retain / v2_release.
Existing Arc<TraitObjectStorage> construction sites continue to work
unchanged — Arc::new(TraitObjectStorage::new(...)) produces a Rust
Arc-wrapped instance whose embedded header sits at refcount=1 unused;
the dispatch arms continue to use Arc::increment_strong_count /
Arc::decrement_strong_count on those bits. The new _new-allocated
raw-pointer bits use the header’s refcount via the HeapElement trait.
The inner value: Arc<TypedObjectStorage> field remains Arc-typed in
E’s scope per Wave 1 §E.6 dispatch contract (the audit’s E-a path
recommends both inner pointers become raw, but D2 owns the inner
*mut TypedObjectStorage flip in lockstep with TypedObjectStorage’s
own Arc-path retirement; E’s struct shape change exposes the
HeapHeader at offset 0 + manual lifecycle so subsequent rounds can
flip the inner field without re-shaping the outer carrier). The
vtable: Arc<VTable> field stays Arc-typed indefinitely under E’s
scope — VTable lifecycle is decoupled from this migration (audit
§E.3 recommended a separate VTable HeapHeader migration if/when
IC devirtualization measurement justifies it).
Fields§
§header: HeapHeaderv2-raw HeapHeader at offset 0 (8 bytes). Refcount/kind/flags.
Initialized to HeapHeader::new(HEAP_KIND_V2_TRAIT_OBJECT) by
_new; for Arc-wrapped instances allocated via
TraitObjectStorage::new the header sits at refcount=1 unused
(the enclosing Arc owns the lifecycle). See struct docstring.
value: *const TypedObjectStorageThe data half of the fat pointer — owned, heap-allocated as a
TypedObject. Always present (never null); universal-dyn
per-method auto-boxing makes the boxed value a real TypedObject
even for scalar concrete types (per §Q25.C.1).
Wave 2 Round 4 D4 ckpt-3 (2026-05-14): inner-field shift from
Arc<TypedObjectStorage> to *const TypedObjectStorage per E
(Round 2) close note + D3 R3a finding D3-1 — the 5th production-
site class (audit-side parallel to D2’s HashMapValueBuf cascade).
The raw pointer was produced by TypedObjectStorage::_new (refcount
initialized to 1 on the HeapHeader at offset 0). The carrier owns
one strong-count share, retired at _drop / auto-derived Drop
via TypedObjectStorage::release_elem(ptr) (NOT Rust Arc::drop).
vtable: Arc<VTable>The vtable half of the fat pointer. Shared via Arc across
all TraitObjectStorage instances built from the same
(impl Trait for Type) pair — vtable construction happens
once per impl, the resulting Arc<VTable> is cached and
cloned into each boxing site. IC stabilizes on
Arc::as_ptr(&vtable) per §Q25.C.6.
Implementations§
Source§impl TraitObjectStorage
impl TraitObjectStorage
Sourcepub fn new(value: *const TypedObjectStorage, vtable: Arc<VTable>) -> Self
pub fn new(value: *const TypedObjectStorage, vtable: Arc<VTable>) -> Self
Build a TraitObjectStorage from its two halves. The caller
owns one strong-count share on the v2-raw value pointer’s
HeapHeader-at-offset-0 refcount AND one strong-count share on
the vtable Arc; the resulting struct owns both shares.
Wave 2 Round 4 D4 ckpt-3 (2026-05-14): value param signature
shifted from Arc<TypedObjectStorage> to *const TypedObjectStorage
per E (Round 2) close note + D3 R3a finding D3-1 — caller produces
the raw ptr via TypedObjectStorage::_new (refcount=1) or by
v2_retain-bumping an existing live ptr. The carrier retires
that share at _drop / auto-derived Drop via
TypedObjectStorage::release_elem(value).
The embedded HeapHeader is initialized to refcount=1 with kind
HEAP_KIND_V2_TRAIT_OBJECT. For Arc<TraitObjectStorage>
instances the header sits unused (the enclosing Arc owns the
lifecycle); the v2-raw _new path is the production carrier
for the on-header refcount lifecycle.
Sourcepub fn _new(value: *const TypedObjectStorage, vtable: Arc<VTable>) -> *mut Self
pub fn _new(value: *const TypedObjectStorage, vtable: Arc<VTable>) -> *mut Self
Wave 2 Agent E (2026-05-14): v2-raw raw-pointer allocator.
Allocates a new TraitObjectStorage on the heap and returns a raw
pointer with refcount initialized to 1. Mirrors the TypedObjectStorage::_new
precedent at heap_value.rs (D1, 2026-05-14) — #[repr(C)] struct
with HeapHeader at offset 0; refcount discipline goes through
v2_retain / v2_release via the HeapElement trait.
Construction-side contract: the caller transfers ownership of one
strong-count share on value: Arc<TypedObjectStorage> and on
vtable: Arc<VTable> to the storage; the storage retires those
shares at _drop (via the in-place drop_in_place on the field
payloads). The inner Arcs follow normal Rust Arc discipline —
only the outer struct’s lifecycle is HeapHeader-managed.
Callers (Wave 2 Round 2): replace the legacy pattern
let arc = Arc::new(TraitObjectStorage::new(value, vtable));
let slot = ValueSlot::from_trait_object(arc);with the v2-raw pattern
let ptr = TraitObjectStorage::_new(value, vtable);
let slot = ValueSlot::from_trait_object_raw(ptr);Sourcepub unsafe fn _drop(ptr: *mut Self)
pub unsafe fn _drop(ptr: *mut Self)
Wave 2 Agent E (2026-05-14): v2-raw raw-pointer deallocator.
Runs drop_in_place on the inner Arc<TypedObjectStorage> value
field and Arc<VTable> vtable field (retires one strong-count
share on each via standard Rust Arc::drop), then deallocates
the struct’s heap memory via Layout::new::<Self>().
Mirrors the TypedObjectStorage::_drop precedent.
§Safety
ptr must point to a live TraitObjectStorage allocated via
Self::_new with no remaining references. Must not be called
more than once on the same pointer; must not be called on
Arc<TraitObjectStorage>-allocated instances (those run
through Rust’s Arc drop machinery + the auto-derived shape).
Sourcepub fn method(&self, name: &str) -> Option<&VTableEntry>
pub fn method(&self, name: &str) -> Option<&VTableEntry>
Convenience: look up a method by name in the vtable. Returns
None for an unknown method (the dispatch tier surfaces this
as a runtime error — under universal-dyn there is no compile-
time ETO-002 for “method not in trait” since the trait’s
declared method set is the surface checked at compile time;
runtime lookup failures indicate a vtable-construction bug).
Sourcepub fn vtable_eq(&self, other: &Self) -> bool
pub fn vtable_eq(&self, other: &Self) -> bool
Identity check for the §Q25.C.2 Self-arg runtime contract.
Arc::ptr_eq on vtable Arcs is the tightest comparison; both
TraitObjectStorage instances must share the same vtable
allocation (which happens when both came from the same
(impl Trait for Type) pair).
Trait Implementations§
Source§impl Clone for TraitObjectStorage
impl Clone for TraitObjectStorage
Source§fn clone(&self) -> Self
fn clone(&self) -> Self
Per-field clone — the inner value ptr’s HeapHeader-at-offset-0
refcount is bumped via v2_retain; the vtable Arc bumps its
strong count by one. Cloning a TraitObjectStorage produces a
fat-pointer carrier that observes the same underlying TypedObject
and dispatches against the same VTable. The cloned struct’s
header is a fresh HeapHeader at refcount=1 (matches Self::new’s
contract — the embedded header is unused on Arc<TraitObjectStorage>
instances; it carries lifecycle only for _new-allocated raw-
pointer instances).
Wave 2 Round 4 D4 ckpt-3 (2026-05-14): inner-value retain shifted
from Arc::clone(&self.value) to v2_retain(&(*self.value).header)
per the value: *const TypedObjectStorage inner-field shift.
1.0.0 (const: unstable) · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source. Read moreSource§impl Debug for TraitObjectStorage
impl Debug for TraitObjectStorage
Source§impl HeapElement for TraitObjectStorage
impl HeapElement for TraitObjectStorage
Source§unsafe fn release_elem(ptr: *const Self)
unsafe fn release_elem(ptr: *const Self)
*ptr. If the refcount reaches
zero, fully deallocate the object (including any nested payload
buffers per the implementor’s drop semantics). Read more