rust-callback-bounds

Diagnose and repair Rust callback trait bounds, HRTBs, and stored callable field types.

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

SYSTEM DOCUMENTATION & REQUIREMENTS

💡 This Skill includes references (resource) components.

What problem does it solve? Rust callback signatures fail with confusing diagnostics like "lifetime may not live long enough", "one type is more general than the other", or E0521 when a closure returns a borrow of its argument, and the wrong fix (hoisting the lifetime or widening for<'a>) silently weakens the API. This Skill routes each symptom to the correct bound shape and prevents dead-end repairs. ## Core Features & Use Cases - Bound selection by return type: A decision table maps what the callback returns (owned value, borrow of the argument, composite over 'a, async result) to the exact bound to write, including for<'a> FnMut(&'a T) -> &'a K with K: ?Sized and AsyncFn versus boxed Send futures. - Diagnostic triage: Worked, compile-checked examples for E0521, E0308, E0282, E0747, E0562, E0277, and the monomorphization recursion-limit failure, with the full rustc 1.98.1 output to match against. - Stored callable guidance: Compares generic F, Box<dyn Fn>, fn pointers, and custom callable traits by size, allocation, and nameability, with measurable size_of and counting-allocator probes. - Use Case: Your sort_by_key(orders, |o| &o.country) fails with "lifetime may not live long enough". The Skill shows the bound is wrong, not the closure, and gives the higher-ranked signature that accepts the bare closure. ## Quick Start Ask the agent to fix the closure lifetime error in my sort_by_key call using the rust-callback-bounds skill.

Frequently Asked Questions about rust-callback-bounds

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

FAQPage Schema
How do I fix "lifetime may not live long enough" from a Rust closure?▼

The error usually means the callback bound is wrong, not the closure. A free type parameter K in FnMut(&T) -> K cannot name the higher-ranked lifetime, so write impl for<'a> FnMut(&'a T) -> &'a K with K: ?Sized instead of annotating the closure.

Why does sort_by_key reject a closure that returns a reference?▼

Vec::sort_by_key takes FnMut(&T) -> K with K fixed outside the binder, so |o| &o.field cannot satisfy it. Use sort_by with a comparator, collect into Vec<&T> first, or write your own function with a for<'a> FnMut(&'a T) -> &'a K bound.

Should I store a closure as a generic F, Box<dyn Fn>, or fn pointer field?▼

Generic F is zero-cost but unnameable in field types (E0747/E0562), Box<dyn Fn> is two machine words and allocates only for non-zero-sized closures, and fn pointers cannot capture. A custom trait with a blanket FnOnce impl gives a nameable, zero-sized option.

Can I use AsyncFn for an async callback in Rust?▼

Yes, impl AsyncFn(&T) -> R works on Rust 1.85+ and accepts async |o| .. closures. However AsyncFn has no stable way to bound its future as Send and is not dyn compatible, so use a boxed for<'a> Fn(&'a T) -> Pin<Box<dyn Future + Send + 'a>> for spawned or type-erased callbacks.

Why does my closure fail the same bound after a let binding?▼

Closure signature inference is positional: a bare let supplies no expectation, so regions resolve to fixed lifetimes and the closure loses its higher-ranked shape. Keep the closure inline at the call site, annotate it as fn(..) or &dyn for<'a> Fn(..), or route it through a generic identity function.

Why does Arc<dyn Fn> not satisfy an F: Fn() bound?▼

std implements Fn for &F and Box<F> but not for Rc or Arc, so Arc<dyn Fn()> fails with E0277. Either change the signature to accept Arc<dyn Fn(..)> directly, or wrap it in a closure: takes(move || a()).