Help with const generic trait bounds

I have a trait that is generic over a usize constant:

trait Solvable<const N: usize> {
    fn solvers(&self) -> Vec<fn(&Self) -> Static<N>>
    where
        Self: Sized;
}

And also have multiple structs which implement this trait for different constants in potentially unrelated ways:

struct A;

impl Solvable<5> for A {
    fn solvers(&self) -> Vec<fn(&Self) -> Static<5>>
    {
         vec![
            <Self as SomethingElse<5>>::solve,
            ...
         ]
    }
}

impl Solvable<23> for A {
    fn solvers(&self) -> Vec<fn(&Self) -> Static<23>>
    {
         vec![
            <Self as AnotherThing<23>>::solve,
            ...
         ]
    }
}

But now I want to do something like this:

pub fn find_solvable(
    identifier: SolvableEnum,
    number: usize
) -> impl Solvable<number> // <-- something crazy like this
{
    match identifier {
        SolvableEnum::StructA => match number {
           5 => A::new() as Solvable<5>,
           23 => A::new() as Solvable<23>,
           _ => panic!("nothing found")
        },
    }
}

With the sole purpose of getting the right solve function from A, then running it on A:

let instanceOfA: Solvable<k> = find_solvable(input_id, input_number);

let solverFns: Vec<fn(&{Solvable<k>}) -> Static<k>> = instanceOfA.solvers();

// In reality, `solverFns` also has ID info, which is why we can single out a 
// single one based on input below
let selectedFn: fn(&{Solvable<k>}) -> Static<k> = 
    find_function(further_user_input, solverFns);

// Will always work because Solvable<k> can always go into functions of type
// fn(&{Solvable<k>}) -> Static<k>
let result: Static<k> = selectedFn(instanceOfA) 

// Out of the woods from this point on...
rest_is_truly_generic_over_N(result)

The problem here is obviously find_solvable. Is there any way to assure the compiler that the return value of find_solvable will be able to provide a function that consumes itself? Of course, I understand that this is not possible with generics, as there is no way to monomorphize find_solvable over every possible number.

By the way, N never exceeds about 255, so usable macro solutions are also appreciated. I was thinking of making one that creates a find_find_solvables which returns one find_solvable for each k: usize, which then maps each identifier one or zero Solvable<k> implementations. But this would also be somewhat problematic, as I would need to add all of the logic happenning downstream of the macro call to the macro call (because there would still be no way to have its return type be Solvable<N>).

I also have no experience writing unsafe code in Rust, so if there is somehow an unsafe solution that leverages this analysis, I would love to hear about it.

Thank you!

You're trying to use const generics in a way that depends on a runtime value, which means it isn't actually const.

You can either design a DynSolvable trait that abstracts over the constness of Solvable, or you can remove the const parameter and have Solvable either always work with whatever the maximum size of N was in practice, or replace whatever was using N with a dynamic storage type[1].


  1. i.e. [usize; N] would become Vec<usize> ↩ī¸Ž

1 Like

Yeah, you're right, it looks like I'm trying to have my cake and eat it too. I was trying to use stack-allocated storage types for performance.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.