rust-type-erasure

Designs and reviews Rust type-erasure maps keyed by TypeId, including borrowed-value stores.

2|1|Updated Aug 19, 2026
One-click install
npx skills add https://github.com/po4yka/rust-skills --skill rust-type-erasure-po4yka
Or copy as Structured Prompt for Agent▼
Please help me install this Agent Skill.
Skill: rust-type-erasure
Source: https://github.com/po4yka/rust-skills/tree/main/skills/rust-type-erasure
Command: npx skills add https://github.com/po4yka/rust-skills --skill rust-type-erasure-po4yka

SYSTEM DOCUMENTATION & REQUIREMENTS

💡 This Skill includes references (resource) components.

What problem does it solve? Rust's Any trait requires T: 'static, so a HashMap<TypeId, Box<dyn Any>> cannot store values that borrow, and the resulting E0597 error blames the caller rather than the map. This Skill guides you through choosing the right erasure design — a lifetime-parameterized enum, a dyn Any map, or a GAT owner/element store — and through reviewing the unsafe cast, variance marker, and extraction layers that make or break soundness. ## Core Features & Use Cases - Symptom-to-section routing: Maps concrete rustc diagnostics (E0597, E0271, E0277, E0117, E0637) and design tasks directly to the fix, including why type_name must never be used as a type key. - The three-rung ladder: Provides compile-checked patterns for a closed enum, an open Box<dyn Any> extensions map, and the exotic GAT bijection store for open sets of borrowed values, with the exact unsafe cast and its SAFETY comment. - Soundness review checklist: Covers the invariance marker PhantomData<UnsafeCell<&'a mut ()>>, the elided get lifetime, Miri verification under Stacked and Tree Borrows, compile_fail doctests, and the unsound extractor shape that detaches a lifetime. - Use Case: You are building an ECS-style world or request-extensions map and hit error[E0597]: argument requires that s is borrowed for 'static. Use this Skill to decide whether to switch to Arc, restructure as an enum, or implement the GAT owner/element map correctly. ## Quick Start Ask the agent to review your TypeId-keyed map or diagnose your E0597 error using the rust-type-erasure skill, and it will route the symptom to the right design rung and verification steps.

Frequently Asked Questions about rust-type-erasure

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I store borrowed values in a Rust map keyed by TypeId?▼

TypeId::of requires T: 'static, so you cannot key on the borrowed type directly. Key on a 'static owner tag and tie it to the borrowed element with a pair of GAT traits, storing Box<dyn AnyDrop + 'a> with an invariance marker.

Why does E0597 blame my local variable instead of the Any map?▼

The T: 'static bound on Any is satisfiable at the generic put definition, so region inference propagates the requirement outward and the first failure appears at the call site. Read the trailing note naming the bound, and change the store rather than extending the local's lifetime.

Can I use std::any::type_name as a map key instead of TypeId?▼

No. type_name has no 'static bound so it compiles where TypeId refuses, but the string is not a unique identifier and erases lifetimes, so two distinct types can share one string. A collision can cast to the wrong type or extend a lifetime.

How do I downcast from Box<dyn Trait> to a concrete type in Rust?▼

Make Any a supertrait and upcast dyn Trait to dyn Any, stable since Rust 1.86, instead of writing an as_any method. Dereference the smart pointer first, because boxed.type_id() returns the TypeId of the Box itself, a bug clippy::type_id_on_box catches only for Box.

When should I avoid the GAT owner/element type map pattern?▼

Avoid it unless the value set is genuinely open and the values genuinely borrow. A closed set belongs in a lifetime-parameterized enum, and owned or Arc-shared values belong in a plain HashMap<TypeId, Box<dyn Any>>, both without any unsafe code.

Why is my extractor over an AnyMap unsound even though Miri passes correct callers?▼

An extractor whose from_world returns Self detaches the result from the world borrow, and a user impl whose GAT is constant in 'a turns the for<'a> closure bound vacuous, yielding a real &'static into freed memory. Return a lifetime-carrying associated type tied to the input borrow instead.