I am currently trying to transform my code into a command line interface. However, I struggle because I have a generic type parameter K, known only at the runtime. Here is what my code looks like:
struct MakeGraph {input: PathBuf, output: PathBuf, other params...}
struct GetStats {input: PathBuf, other params...}
trait Run {
fn run<K: Kmer>(&self);
}
impl Run for MakeGraph {...}
impl Run for GetStats {...}
pub fn main() {
let cli = Cli::parse();
let k_size = cli.k_size;
match &cli.command {
Commands::MakeGraph { ... } => {
let operation = MakeGraph {...};
match k_size {
1 => operation.run::<Kmer1>(),
2 => operation.run::<Kmer2>(),
3 => operation.run::<Kmer3>(),
4 => operation.run::<Kmer4>(),
//...
_ => panic!("Unsupported k-mer size: {}", k_size),
}
_ => {}
},
}
}
This is not doable in practice, as kmer_size can be between 1 and 64, and I have more operations. What I really want to do looks more like this:
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)]
struct myKmerSize<const K: usize>;
impl<const K: usize> kmer::KmerSize for myKmerSize<K> {
fn K() -> usize {
K
}
}
type myKmer = match k_size {
1..=4 => type kmer::VarIntKmer<u8, myKmerSize<k_size>>,
5..=8 => kmer::VarIntKmer<u16, myKmerSize<k_size>>,
9..=16 => kmer::VarIntKmer<u32, myKmerSize<k_size>>,
17..=32 => kmer::VarIntKmer<u64, myKmerSize<k_size>>,
32..=64 => kmer::VarIntKmer<u128, myKmerSize<k_size>>,
_ => panic!("Unsupported k-mer size: {}", k_size),
}
match &cli.command {
Commands::MakeGraph { input, output, stranded } => {
let operation = MakeGraph{...};
operation.run::<myKmer>();
}
...
},
However, it does not work, as it. I would be grateful for any suggestions.
For more information, the structure VarIntKmer
is defined like the following in the crate debruijn. It is composed of an integer storage allowing to memorize kmer_size
DNA nucleotides (2 bits per nucleotide) and of some phantom data indicating the number of bases stored. I have forked the crate so I can slightly modify it if necessary.
/// Helper trait for declaring the K value of a Kmer. Will be removed when const generics are available
pub trait KmerSize: Ord + Hash + Copy + fmt::Debug {
#[allow(non_snake_case)]
fn K() -> usize;
}
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Serialize, Deserialize)]
pub struct VarIntKmer<T: PrimInt + FromPrimitive + IntHelp, KS: KmerSize> {
pub storage: T, // the smallest integer type able to fit 2*KS::K() bits
pub phantom: PhantomData<KS>,
}