Partial specialization of a generic struct

Why does the following code not compile?

pub struct GenVal<T>(pub T);

impl<T> GenVal<T> { 
    pub fn value(&self) ->&T {
        println!("Generic value() function called.");
        &self.0
    }
}

impl GenVal<f64> {
    pub fn value(&self) ->&f64 {
        println!("f64 value() function called.");
        &self.0
    }
}

fn main() {
    let z = GenVal(3.14);
    let z_val = z.value();
}

Playpen link: Rust Playground

1 Like

Okay, I think I jumped the gun on this one. Rust probably does not support partial specialization (and operator overloading). The following does not work either...

use std::fmt::Debug;

#[derive(Debug)]
struct SGen<T: Debug>(T); // Generic type `SGen`.


fn generic(s: SGen<i32>) {
    println!("i32 version called. s={:?}", s);
}

fn generic<T: Debug>(s: SGen<T>) {
    println!("Generic version called. s={:?}", s);
}

fn main() {
    generic(SGen(1.0));
    generic(SGen(6));
}

Instead you have to do the following...

use std::fmt::Debug;

#[derive(Debug)]
struct SGen<T: Debug>(T); // Generic type `SGen`.


fn gen_spec_i32(s: SGen<i32>) {
    println!("i32 version called. s={:?}", s);
}

fn generic<T: Debug>(s: SGen<T>) {
    println!("Generic version called. s={:?}", s);
}

fn main() {
    generic(SGen(1.0));
    gen_spec_i32(SGen(6));
}

So the original code can be made to work like so...

pub struct GenVal<T>(pub T);

impl<T> GenVal<T> { 
    pub fn value(&self) ->&T {
        println!("Generic value() function called.");
        &self.0
    }
}

impl GenVal<f64> {
    pub fn value_f64(&self) ->&f64 {
        println!("f64 value() function called.");
        &self.0
    }
}

fn main() {
    let z = GenVal(3.14);
    let _z_val_g = z.value();
    let _z_val = z.value_f64();
}

I ran into this problem too and figured I needed negative trait bounds to make it work, so I just ended up ditching blanket implementations with generics for now.

Perhaps the issue is that specialisation isn't applying to generic parameters; the GenVal<T> type encapsulates GenVal<f64>, because f64 is in the scope of T, so either impl is equally valid.

If I add another type parameter to GenVal, and use something different for the f64 implementation then it works because GenVal<T, ()> does not encapsulate GenVal<f64, Option<()>>. If the second type parameter is the same as the blanket impl then it fails again with the same error.

There is plan to support specialization : rfcs/0000-impl-specialization.md at impl-specialization · aturon/rfcs · GitHub. But it is not available in stable Rust yet.

Rust does not support function overloading, but it support custom operator definition by implementing Add, Sub, Mul, ... traits

I guess so but coming from a C++ background I feel C++ does the right thing in this case. It uses the most specialized implementation (GenVal<f64>::value()) from all available implementations when possible. The thinking is that the programmer will not bother writing a specialized version of a function unless it is beneficial over the generic version somehow.

Thanks for sharing the negative trait link but I still think the C++ way of doing this looks more elegant.

1 Like

Thanks for sharing this link. I think it is a better solution for this problem.