My use case is for advent_of_code_traits. Where I have a trait like
Solution<const Day: u32> {
type Part1Output;
type Part2Output;
fn part1(input: /* we'll get there */) -> Self::Part1Output;
fn part2(input: /* we'll get there */) -> Self::Part2Output;
}
The const generic parameters effectively mean I have a different trait for each day. The part1 and part2 fns are for each part of the day (in advent of code, each day there is a puzzle with 2 parts).
I'd like my users to implement the Solution
trait and have a default run
method that will...
- read input from a file
- run it through their solutions
- print a summary to the console
But now we need to talk about parsing:
I'd like to support parsing the text input to a ParsedInput
type for each part of a day, user's choice.
I'd also like to support using the same ParsedInput
type for both days to avoid duplication.
Here's a trait for different parsing for each part of a day.
trait ParseEachInput<const Day: u32, const Part: u32> {
type Parsed;
fn parse(input : &str) -> Self::Parsed;
}
Let me alsot introduce the trait for when the parsing is the same for both parts:
trait ParseInput<const Day: u32> {
type Parsed;
fn parse(input : &str) -> Self::Parsed;
}
The difference is an extra const generic parameter Part
when parsing Each part differently.
This lets the user implement it twice, once for each part, specifying 1 and then 2 for Part
.
e.g. for day 7:
impl ParseEachInput<7, 1>
impl ParseEachInput<7, 2>
Now the actual problem (sorry this is taking so long, I'm honestly kind of mortified how convoluted this is )
I can provide a default run implementation (somewhat) simply like this:
fn run(input: &str) {
let part1_parsed_input = <Self as ParseEachInput<Day, 1>>::parse_input(input);
let part2_parsed_input = <Self as ParseEachInput<Day, 2>>::parse_input(input);
// ^^^^ note the Each
let part1_output = <Self as Solution<Day>>::part1(&part1_parsed_input);
let part2_output = <Self as Solution<Day>>::part2(&part2_parsed_input);
But run
will always parse the input twice!
What I want is a way to specialize the default run impl to be more efficient when it knows ParseInput
is implemented (i.e. both parts want to share the same parsed input) like this:
fn run(input: &str) {
let parsed_input = <Self as ParseInput<Day>>::parse_input(input);
// ^^^^^^^^^^ no Each
let part1_output = <Self as Solution<Day>>::part1(&parsed_input);
let part2_output = <Self as Solution<Day>>::part2(&parsed_input);
Self::report(part1_output, part2_output);
}
As it stands I will get a type mismatch when trying to compile the above:
error[E0308]: mismatched types
--> src/lib.rs:330:59
|
330 | let part1_output = <Self as Solution<Day>>::part1(&parsed_input);
| ^^^^^^^^^^^^^ expected ParseEachInput::Parsed, found ParseInput::Parsed
|
= note: expected reference `&<Self as ParseEachInput<Day, 1_u32>>::Parsed`
found reference `&<Self as ParseInput<Day>>::Parsed`
This is a lot of code to throw around in a forum post.
There is a repo here and an issue
I saw that feature(specialization)
is unsound. I'd be willing to use nightly if min_specialization applies here but I have been struggling to learn how to use it at all.
I would greatly appreciate any help for my admittedly bizarre and largely unnecessary use case Thank you for indulging me