(This is the Rust version, for Scala version, please go to In Scala or other JVM language, which type declaration is closest to a type declaration nested in a generic class in C# or C++?)
When writing a library that relies heavily on generic/template in C# or C++, one trick I often use is to declare several structs that uses the same generic type arguments as nested types under a static class.
e.g. these declaration (using C# as an example):
public struct Gen1<T1, T2, T3>
{
}
public struct Gen2<T1, T2, T3>
{
}
is functionally equivalent to this:
public static class Ts<T1, T2, T3>
{
public struct Gen1
{
}
public struct Gen2
{
}
}
Namely, Ts<int, int, string>.Gen1 is converted to a specialised class by the compiler, with its own static members. Ts<int, int, String> and Ts<int, int, int> becomes 2 different classes that shares nothing except names.
I have not found similar abstract in most other languages (the only exception is LEAN4, where the static generic class becomes a section: https://lean-lang.org/lean4/doc/sections.html). When converting such code to Rust, most LLMs will suggest using mod feature (which has no generic/template) and give an obviously defective answer, e.g. the following answer from Claude 3.5:
mod container<T> {
pub struct Holder {
value: T,
}
impl<T> Holder {
pub fn new(value: T) -> Self {
Holder { value }
}
pub fn get(&self) -> &T {
&self.value
}
}
}
What's the easiest/shortest way to convert such declaration?
Rust does not support defining structs within structs (and yeah mod
can't introduce generics). So the Rust equivalent would look like the first C# snippet; Gen1
and Gen2
need to be defined seperately with their own generic parameters.
If you want Ts
to have some strong association with Gen1
and Gen2
, you can do that with an associated type declaration but it can only be through a trait implementation (until inherent associated traits are supported). Here's how that could look in Rust:
struct TsGen1<T1, T2, T3> {
// ... fields
}
struct TsGen2<T1, T2, T3> {
// ... fields
}
struct Ts<T1, T2, T3> {
// ... fields
}
trait Gen {
type Gen1;
type Gen2;
}
impl<T1, T2, T3> Gen for Ts<T1, T2, T3> {
type Gen1 = TsGen1<T1, T2, T3>;
type Gen2 = TsGen2<T1, T2, T3>;
}
Then you could refer to the "nested" types as Ts<A, B, C>::Gen1
. Helpful if you want that organization, but not helpful if you're just trying to simplify definitions.