Specialization of a generic method for a struct?

I'm currently reading through Rust By Example, and on the page about generics (http://rustbyexample.com/generics/impl.html) it seems like the text is hinting that it would be possible to create specialized implementations for a struct. I changed the code as follows to try it out:

struct GenTup<T>(T,);

impl <T> GenTup<T> {
    fn value(&self) -> &T {
        let &GenTup (ref val) = self;
        val
    }
}

impl GenTup<f32> {
    fn value(&self) -> &f32 {
        println!("This is a specialized version for f32");
        let &GenTup (ref val) = self;
        val
    }
}

fn main() {
    let y = GenTup(3i32);
    let z = GenTup(5f32);

    println!("{}, {}", y.value(), z.value());
}

I get these errors when compiling:

hello.rs:24:37: 24:44 error: multiple applicable methods in scope [E0034]
hello.rs:24     println!("{}, {}", y.value(), z.value());

...

hello.rs:4:5: 8:6 note: candidate #1 is defined in an impl for the type `GenTup<_>`
hello.rs:4     fn value(&self) -> &T {

...

hello.rs:12:5: 17:6 note: candidate #2 is defined in an impl for the type `GenTup<f32>`
hello.rs:12     fn value(&self) -> &f32 {

Is this possible? I looked around but found posts talking about specialization of traits and not a struct impl, so they didn't seem directly applicable. Hope this isn't too stupid of a question. :slight_smile:

2 Likes

There's no specialization support currently. You can implement different sets of methods for different Ts but the compiler won't attempt to resolve the ambiguity in case they overlap.

Oh, I see! What would be the right way to do this in Rust?

I don't follow. Do what? I guess we have to live with the lack of specialization for now. Not sure what the author was alluding to exactly there. Rust By Example is a bit dated now it may mislead you in little ways.

The example is to present contrast between what a specialized and unspecialized impl looks like and to point out why <T> must be used twice in a generic impl which is not necessarily obvious.

A workaround is to leave the type generic and specialize all the impls without providing a generic impl: playpen.

Specialization is apparently in the works.

I see, I probably misunderstood the intent behind the sample. What I meant about the "right way", is how would a Rust developer work around this using idiomatic Rust code? What are devs doing right now? I'm curious, but I'm not familiar enough with the language to really know the right way to do something like this :wink:

That would be really great! I've been following the language for a while and things have really improved massively just over the last year alone. I'm really excited for the day when I can use this instead of C or C++ for dropping down to native code. Right now the main things holding me back from actually using it is the lack of first-class support for Android and iOS, and I'm not sure about the current implementation of wrapping integers; it seems very much "in progress" and I've seen that some people have had issues with it.

Edit: if anyone is curious by what I mean by "first class support", I mean that prebuilt compilers should be available and tested, and that there should be hooks in place to easily use the compilers from an NDK build or an XCode build so I can consume these binaries in my platform-specific code. OTOH I certainly don't expect things like the Android Java APIs to be available from Rust without implementing my own wrappers. Once 1.0 stable lands I'm going to take a look at this myself as I'm curious. :wink:

Sorry @loes, but I didn't understand what you meant with wrapped integers.
I am also very excited to use Rust and get rid of C++ (though C++11 is quite nice) and I would like to understand what it is now missing to become my first language.

This thread is interesting: Ergonomics of wrapping operations - #23 by wackywendell - ideas (deprecated) - Rust Internals

So based on that it seems that there are issues with overflowing integer ops and wrapping integers. I don't know if these have been resolved in 1.0 or if it will be later -- perhaps someone in the know can chime in?

Personally this is something I'd rather not have to care about -- not once have I ever encountered a bug caused by overflow (though I have encountered plenty caused by incorrect exception handling, null pointer exceptions, etc..., so I'm really excited for the improvements Rust brings to the table here!), so this seems iffy to me. I'm not convinced that it's worth the ergonomic issues. On the other hand I can understand why this is important for some projects and why it's important for the Rust community to get this down pat. This community seems to have reached the right decisions in many areas and that's one of the reasons I'm so interested. :slight_smile:

A proposal for specialization has been introduced.

Sorry for necrobumping this, but my question will have better context being attached here.

Now when specialization proposal is in nightly build, trying to do specialization in a way Loes tried, but with default keyword and feature directive to compiler I still get the same error. Rust Playground

Does new specialization work only for traits implementations? Most examples from rfc1210 use traits, just wonder why it doesnt work for structs too.

AFAIK, specialization as currently implemented in nightly is only for traits, and doesn't effect your example at all.

Specialization doesn't apply to inherent methods (that is, non-trait methods) right now.

Are there any plans to support specialization outside of traits? (...he asked a mere year after the last post in the thread)

@jlgerber can you give us an example of what you're trying to achieve?

If you want to specialize methods/functions/whatever then that's usually because they are taking in several different types of input... Which is essentially the exact reason to extract that behaviour out into a helper trait.

For example (using config crate):

pub trait GetConf<T> {
    fn get_conf(&self, key: &str) -> Result<T>;
}
<...>
impl<u16> GetConf<u16> for Foo {
    fn get_conf(&self, key: &str) -> Result<u16> {
        self.conf
            .get_int(key)?
            .try_u16()?
    }
}

impl<'de, T> GetConf<'de, T> for Foo
where
    T: Deserialize<'de>,
{
    fn get_conf(&self, key: &str) -> Result<T> {
        self.conf.get(key)?
    }
}