context-trait 0.1.1

Runtime-synthesized trait instances scoped to callbacks
Documentation
  • Coverage
  • 100%
    14 out of 14 items documented9 out of 9 items with examples
  • Size
  • Source code size: 22.54 kB This is the summed size of all the files inside the crates.io package for this release.
  • Documentation size: 2.94 MB This is the summed size of all files generated by rustdoc for all configured targets
  • Ø build duration
  • this release: 1m 26s Average build duration of successful builds.
  • all releases: 1m 4s Average build duration of successful builds in releases after 2024-10-23.
  • Links
  • Homepage
  • joshburgess/reify-reflect
    0 0 0
  • crates.io
  • Dependencies
  • Versions
  • Owners
  • joshburgess

Swap out a type's Ord, Hash, Display (or any user-defined trait) for one block of code, without newtype boilerplate.

Sometimes you want to sort a Vec<i32> descending, hash a String case-insensitively, or render a number with a custom prefix, but only for one operation. The orthodox Rust answer is to wrap the value in a newtype that implements the trait differently. That works, but it requires a new struct, manual impls, and you lose access to all the existing impls on the inner type.

This crate offers a lighter alternative. [WithContext<T, Ctx>] is a wrapper that pairs a value with a context, a small Copy struct of function pointers that supplies the relevant trait implementation. Standard library APIs that take Ord / Hash / Display work unchanged; the context decides the behavior.

Three built-in contexts cover the common cases:

  • [OrdContext<T>] supplies a custom comparator.
  • [HashContext<T>] supplies a custom hasher.
  • [DisplayContext<T>] supplies a custom formatter.

And three corresponding macros wrap up the lift / call / project dance:

  • [with_ord!]
  • [with_hash!]
  • [with_display!]

For traits beyond these three, [impl_context_trait!] generates a new context type for an arbitrary trait of yours.

Why function pointers?

Contexts hold fn pointers, not Box<dyn Fn>. This makes the wrapper Copy regardless of T, which is what lets BTreeSet, slice::sort, and HashMap accept it without complaint. It also means the comparator must be stateless. If you need captured state, reach for a newtype.

See docs/phase3-context-trait.md for the full design rationale.

Examples

Sort a slice descending without a newtype:

use context_trait::{with_ord, OrdContext, WithContext};

let items = vec![3i32, 1, 4, 1, 5];
with_ord!(items, |a: &i32, b: &i32| b.cmp(a),
    |wrapped: &[WithContext<i32, OrdContext<i32>>]| {
        let mut sorted = wrapped.to_vec();
        sorted.sort();   // uses the descending comparator
        let values: Vec<i32> = sorted.into_iter().map(|w| w.inner).collect();
        assert_eq!(values, vec![5, 4, 3, 1, 1]);
    });