Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.
Quelldateien prüfen
Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.
Mit Codex oder Claude installieren Kopieren Sie diesen Prompt, fügen Sie ihn in Codex, Claude oder einen anderen Assistant ein und lassen Sie die Skill-Seite prüfen und installieren.
Ein direkter Befehl überspringt den Prüf-Prompt. Prüfen Sie die Quelle, bevor Sie ihn ausführen.
Use when the user writes generic functions / types / impl blocks, needs trait bounds, where clauses, const generics, or wonders about monomorphization vs dyn dispatch trade-offs. Prevents over-constraining with `'static`, mixing up trait-bound syntax, or assuming generic Rust code emits runtime polymorphism (it doesn't, it's monomorphized). Covers: type parameters `<T>`, trait bounds `T: Trait`, multiple bounds with `+`, `where` clauses, monomorphization implications (code-size, compile-time), const generics (`N: usize`, where they can/cannot appear), generic lifetime parameters mixed with type parameters, default generic types (`<T = Default>`). Keywords: generic, "type parameter", "<T>", "trait bound", "where clause", "T: Trait", monomorphization, "code bloat", "const generic", "N: usize", "generic function", "generic struct", "generic impl", "default generic type", PhantomData generic, "compile time generics", "expanded at compile time", "generic over".
license
MIT
compatibility
Designed for Claude Code. Requires Rust 1.85+, edition 2024.
metadata
{"author":"OpenAEC-Foundation","version":"1.0"}
rust-syntax-generics
Generic programming in Rust: type parameters <T>, trait bounds, where clauses, const generics <const N: usize>, default generic types, and how monomorphization shapes the trade-off between zero-cost abstraction and binary size. Generics are a feature: every concrete instantiation produces its own machine code. Runtime polymorphism in Rust requires (see [[rust-syntax-trait-objects]]).
compile-time
dyn Trait
Cross-references: [[rust-syntax-traits]] (trait declarations, blanket impls, orphan rule) [[rust-syntax-trait-objects]] (dyn Trait runtime alternative) [[rust-syntax-lifetimes]] (lifetime parameters mixed with type parameters) [[rust-syntax-gats]] (Generic Associated Types, built on this skill) [[rust-core-language-versions]] (which generic features stabilized when).
When to use this skill
User writes fn foo<T>(x: T), struct Foo<T>, enum Result<T, E>, or impl<T> Foo<T>
User needs to constrain a type parameter with a trait: T: Display + Clone
User wonders whether to use a where clause vs inline bounds
User asks "is this expanded at compile time" / "what's the runtime cost of generics"
User defines an array-size-generic API: fn process<const N: usize>(buf: [u8; N])
User encounters confusing error: T does not live long enough and over-applies 'static
User asks about default generic types (Vec<T, A = Global>, HashMap<K, V, S = RandomState>)
User mixes generic lifetime and type parameters: struct Wrapper<'a, T>
For trait declarations themselves (defining trait Foo {}) see [[rust-syntax-traits]]. For runtime polymorphism (heterogeneous collections) see [[rust-syntax-trait-objects]].
The one rule that explains everything
Generics in Rust are monomorphized at compile time. The compiler generates one distinct copy of the generic item for each unique set of concrete type arguments it is instantiated with. From the research: "Rust resolves all type parameters at compile time. Every concrete instantiation of a generic function produces a distinct symbol in the binary. This delivers zero-cost abstraction at the price of compile time and binary size." Generic Rust has zero runtime dispatch cost but non-zero binary cost.
If you want runtime polymorphism (one function pointer, multiple types behind it), you must explicitly opt into dyn Trait (see [[rust-syntax-trait-objects]]).
Quick reference table
Construct
Syntax
Where
Generic function
fn foo<T>(x: T) -> T
item declaration
Generic struct
struct Wrap<T> { value: T }
item declaration
Generic enum
enum Either<L, R> { Left(L), Right(R) }
item declaration
Generic impl block
impl<T> Wrap<T> { ... }
impl declaration
Generic impl with bound
impl<T: Display> Wrap<T> { ... }
impl header
Trait bound (inline)
fn foo<T: Display>(x: T)
parameter list
Multiple bounds
T: Display + Clone + Send
bound position
Where clause
fn foo<T>(x: T) where T: Display + Clone
after signature
Const generic
fn arr<const N: usize>(a: [u8; N])
parameter list
Default generic type
struct Vec<T, A: Allocator = Global>
item declaration
Lifetime + type
struct Ref<'a, T> { r: &'a T }
item declaration
HRTB
for<'a> Fn(&'a T)
bound position
Sized opt-out
fn foo<T: ?Sized>(x: &T)
bound position
Type parameters
A type parameter is a placeholder filled in by the compiler at every call site. Type parameters use single uppercase letters by convention (T, U, K, V, E) but any identifier in UpperCamelCase is legal.
fnlargest<T: PartialOrd>(list: &[T]) -> &T {
letmut largest = &list[0];
foritemin list {
if item > largest {
largest = item;
}
}
largest
}
ALWAYS list every type parameter in <...> after the item name. The bound T: PartialOrd is required because > calls PartialOrd::gt, and without the bound gt would not be callable.
NEVER write impl Point<T> without impl<T>: it would attempt to impl on a concrete type named T (or fail with E0412).
Trait bounds
Trait bounds restrict which concrete types may be substituted for a type parameter. Without a bound, the only operations callable on T are those defined for all types (move, drop, mem::size_of).
Inline vs where clause
Both syntaxes are equivalent. Inline bounds are concise for one or two simple bounds:
ALWAYS use a where clause when bounds reference associated types (I: Iterator<Item = T>) or when bounds span more than ~60 characters; readability is the deciding factor. NEVER write the same bound twice in inline AND where form for the same parameter; pick one.
The + joiner works in inline and where positions identically.
Associated-type bounds
Bounds on associated types use the Trait<Item = X> form (introduced 1.0) or the newer Trait<Item: SomeBound> form (1.79+):
fnsum<I>(iter: I) ->i32where
I: Iterator<Item = i32>, // exact-type associated bound
{
iter.sum()
}
fnprint_items<I>(iter: I)
where
I: Iterator<Item: Display>, // bound on associated type, since 1.79
{
forxin iter { println!("{x}"); }
}
Monomorphization in practice
Every distinct concrete instantiation produces a distinct compiled function. Given:
fnid<T>(x: T) -> T { x }
fnmain() {
leta = id(1u32); // emits id::<u32>letb = id(1u64); // emits id::<u64>letc = id("hi"); // emits id::<&str>
}
The binary contains three distinct copies of id. Implications:
Zero runtime cost: each call is a direct call to a specialized function. Inlining works as usual.
Larger binary: every type combination multiplies code size. A 50-line generic function instantiated for 20 types adds ~1000 lines of generated assembly.
Longer compile times: codegen runs once per instantiation.
No vtable, no fat pointer: a T value has the exact size and layout of its concrete type.
ALWAYS prefer generics over dyn Trait for hot paths where dispatch cost matters. Choose dyn Trait when (1) the type set is open-ended at runtime, (2) heterogeneous collections are needed (Vec<Box<dyn Drawable>>), or (3) binary size dominates.
Const generics
Const generics are type parameters that hold values, not types. Stable for primitive integer types, char, and bool since Rust 1.51 (Min Const Generics). Generic const expressions remain unstable.
Generic const expressions (feature(generic_const_exprs)): you cannot write [u8; N + 1] or [u8; { N * 2 }] in stable Rust. NEVER use these on stable; the feature is unstable as of Rust 1.87.
Const generic types beyond integers/bool/char: structs, floats, and user-defined types are not allowed as const generic parameters on stable.
Defaults that depend on other const params: const M: usize = N is rejected.
The bound T: 'a reads "any references inside T outlive 'a". For most owned types (String, Vec<u8>, i32), this bound is automatically satisfied because they contain no references. ALWAYS write T: 'a (or rely on the implicit T: 'a that the compiler inserts on generic types holding &'a T) instead of jumping to T: 'static. See [[rust-syntax-lifetimes]] for the full story.
Higher-Ranked Trait Bounds (HRTB)
When a closure must accept references of any lifetime:
The for<'a> quantifies over all lifetimes; without it, the type inference would pin a single concrete 'a.
Sized and ?Sized
By default, every type parameter has an implicit T: Sized bound. From the Reference: "Type parameters (except Self in traits) are Sized by default, as are associated types. These implicit Sized bounds may be relaxed by using the special ?Sized bound."
NEVER write the redundant explicit T: Sized bound; the compiler already inserts it. ONLY use T: ?Sized when you genuinely need to accept dynamically-sized types like str, [T], or dyn Trait. Restrictions: ?Sized types can only be used behind a pointer (&T, &mut T, Box<T>, Rc<T>, etc.).
PhantomData and generic markers
When a generic type does not actually hold a T, the compiler refuses to accept the parameter unless PhantomData<T> declares the intent:
use std::marker::PhantomData;
structTagged<T> {
id: u64,
_marker: PhantomData<T>, // tells the compiler: T is significant
}
PhantomData<T> has zero size (verified: size_of::<PhantomData<T>>() == 0). Use it to:
Express that a type parameter influences variance, drop-check, or trait selection.
Carry a phantom lifetime: PhantomData<&'a ()>.
Mark thread-safety: PhantomData<*mut ()> makes a type !Send + !Sync.
See the variance table in [[rust-syntax-lifetimes]] for how PhantomData<T> is covariant in T.
Decision tree: generics vs trait objects
Need polymorphism?
├── At compile time (type set known) ──> generics (this skill)
│ ├── Performance-critical hot path ─> generics, monomorphized
│ └── Many type combinations? ──────> watch binary size, may switch to dyn
└── At runtime (type set open / heterogeneous) ──> dyn Trait (see rust-syntax-trait-objects)
├── Heterogeneous collection ─────> Vec<Box<dyn Trait>>
└── Plugin / unknown-at-compile types ──> dyn Trait
Edition 2024 interactions
Generics interact with edition 2024 in two places:
RPIT lifetime capture: in edition 2024, -> impl Trait implicitly captures all in-scope generic parameters, including lifetimes. Pre-2024 code that relied on impl Trait not capturing a lifetime needs use<> syntax to preserve old behavior. See [[rust-syntax-traits]] for impl Trait semantics and cargo fix --edition for automatic migration.
Never-type fallback: !-to-any coercions now fall back to ! (was ()). This rarely affects generic code directly but can change which generic impls are selected when ? returns Err(_) -> !.