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!