add match_ty based on cast_fns
Fixes #35.
This PR is based on #36, I'd like to perform rebase when #36 landed.
- add
try_cast_from,get_cast_fnsforTryCast* - every
TryCasttrait should implementedCAST_METHOD(just for test, tell which trait is actually calling on),can_cast,unchecked_fromandunchecked_to, other methods would derived from these methods - add
can_cast!,get_cast_fns!andmatch_ty!macros
type From = u8;
type To = u8;
assert!(can_cast!(From, To));
if let Some((from, to)) = get_cast_fns!(From, To) {
assert_eq!(from(1), 1);
assert_eq!(to(1), 1);
}
let result: From = match_ty!(To, {
u8 => from(1u8),
u16 => from(2u16), // this would never called
_ => unreachable!()
});
It would be better naming
- current
match_typetomatch_cast - and
match_tyin this PR tomatch_type
But for non-breaking change, we naming it match_ty
This would somehow also address #24 by implementing cast_from!
Perform a compile-time style match over a single source type against one or more candidate destination types, executing the first matching branch.
This macro is the type-level analogue to [match_type!], but instead of
matching on the runtime value of an expression it examines only the static
type From (the first argument). Each arm lists one or more concrete
destination types separated by |. If any of those destination types can
be (symmetrically) cast to/from From (i.e. they are identical under this
crate's rules), the corresponding branch expression is evaluated and its
value becomes the macro's result.
Internally this uses [can_cast!] (fast path) and/or [get_cast_fns!]
(when you request the function pointers) to test feasibility. Untaken
branches compile away completely; there is no runtime overhead after
monomorphization. No data transformation occurs: only identity casts for
concretely equal types (respecting lifetime / lifetime‑free constraints)
are considered matches.
Syntax
Basic form (boolean style matching on type only):
match_ty!(From, {
Type1 => expr1,
Type2 | Type3 => expr2,
_ => default_expr,
})
A leading | before the first type in an arm (| Type1 | Type2 => ...) is
also accepted for stylistic consistency with normal Rust pattern groups.
Function‑capturing form (obtain zero‑cost cast function pointers):
match_ty!(From, (from_fn, to_fn), {
TargetType => expr_using_from_fn_and_to_fn,
_ => fallback,
})
In a matching arm, from_fn has type fn(TargetType) -> From and to_fn
has type fn(From) -> TargetType. In non‑matching arms those names are not
bound (the code for that arm is never executed anyway). You may ignore one
of them with _.
An entirely empty arm set is permitted: match_ty!(T, {}) expands to ().
A final default arm (_ => ...) is otherwise required because you cannot
enumerate all possible types.
Differences from [match_type!]
- Operates only on types; no value is evaluated or bound.
- Branch expressions cannot pattern‑match on a value of the matched type.
- Directly returns the branch expression (no intermediate
Result).
Captured function pointer ordering
The tuple captured internally (and exposed via your (from_fn, to_fn)
pattern) is (fn(Target) -> From, fn(From) -> Target). Naming them from
and to is a common convention used in the examples below.
Examples
Basic categorization:
use castaway::match_ty;
fn classify<T: 'static>() -> &'static str {
match_ty!(T, {
u8 | i8 => "byte",
u16 => "short",
_ => "other",
})
}
assert_eq!(classify::<u8>(), "byte");
assert_eq!(classify::<i8>(), "byte");
assert_eq!(classify::<u16>(), "short");
assert_eq!(classify::<u32>(), "other");
Specializing while capturing cast functions (note _ ignores the second):
use castaway::match_ty;
fn default_value<T: 'static + Default>() -> T {
match_ty!(T, (from, _), {
u8 => from(1u8),
i32 => from(2i32),
_ => T::default(),
})
}
assert_eq!(default_value::<u8>(), 1);
assert_eq!(default_value::<i32>(), 2);
assert_eq!(default_value::<u16>(), 0); // via Default
Accessing both conversion directions directly:
use castaway::match_ty;
fn maybe_round_trip<T: 'static + Copy>() -> Option<(fn(u32)->T, fn(T)->u32)> {
match_ty!(T, (from, to), {
u32 => Some((from, to)),
_ => None,
})
}
assert!(maybe_round_trip::<u32>().is_some());
assert!(maybe_round_trip::<u16>().is_none());
Empty usage (rare):
use castaway::match_ty;
const _: () = match_ty!(u8, {}); // Expands to ()