Generic-upper-bound: A stable generic_const_exprs workaround crate for library internals

https://docs.rs/generic-upper-bound/latest/generic_upper_bound/

I've recently published a stable workaround crate for generic_const_exprs that allows you to "temporarily" get a const generic usize that is an upper bound for an arbitrary const usize (passed as an associated const).

Because this is hard to explain, here is an example that concatenates &'static strs at compile time, even if their value depends on generic parameters:

use generic_upper_bound as gub;
pub trait MyTrait {
    const SOME_STR: &'static str;
}
struct Concat<A, B>(A, B);
gub::impl_accept_upper_bound! {
    impl{A: MyTrait, B: MyTrait} Concat<A, B>;

    const DESIRED_GENERIC: usize = A::SOME_STR.len() + B::SOME_STR.len();

    const EVAL<const UPPER: usize>: &'static [u8] = &{
        let mut out = [0u8; N];  // copy over the bytes and promote
        let (l, r) = (A::SOME_STR.as_bytes(), B::SOME_STR.as_bytes());
        let (l_out, r_out) = out.split_at_mut(l.len());
        l_out.copy_from_slice(l);
        r_out.split_at_mut(l.len()).0.copy_from_slice(r);
        out
    };
}
impl<A: MyTrait, B: MyTrait> MyTrait for (A, B) {
    // evaluate the upper bound acceptor, trim trailing nul bytes
    // and convert to string
    const SOME_STR: &'static str = match core::str::from_utf8(
        gub::eval_with_upper_bound::<Concat<A, B>>()
            .split_at(gub::desired_generic::<Concat<A, B>>())
            .0,
    ) {
        Ok(s) => s,
        _ => unreachable!(),
    };
}
impl MyTrait for () {
    const SOME_STR: &'static str = "ABC";
}
impl MyTrait for i32 {
    const SOME_STR: &'static str = "123";
}
let concatenated: &'static str = <((), i32)>::SOME_STR;
assert_eq!(concatenated, "ABC123");

Similar functionality can be found already in crates like const-str, however they error in generic code.

The code is pretty terse and verbose and I might write a macro for it later, but for now I'm pretty happy with what it can do. The main use case I can think of is library internals (for example, you could build concatenate custom vtables using this, assuming they are layout-compatible with slices of function pointers), so I hope that someone finds this useful!

3 Likes

Update: I added a simple macro_rules! that automatically generates the Const implementation, so the code should be a lot more readable now.