Generic type known only at runtime

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>,
}

Mixing compile time (generics) and runtime (parsing the size from cli) isn't a great idea.

Instead of:

struct MyKmerSize<const K: usize>;

Try a plain:

struct MyKmerSize(size: usize);

In the operation.run itself, match against the ranges (1..=4, 32..=64), and instantiate the new_uninit_slice() using any of the u8 / u32 / u128. Populate it, and call .assume_init().

Then do whatever it is you need to. No type MyKmer = match ... shenanigans.

Thanks for your answer. However, the public crate 'debruijn' heavily relies on the Trait Kmer, and its static method giving the kmer size. What you suggest would require to turn it into an instance method, each kmer storing its own size. This seems impossible without rewriting the whole crate. Also, even if it is only known at compile-time, all kmers share one unique size.

Try this, then - tedious, but straightforward.