ideasman42
ideasman42

Reputation: 48248

Generic functions in Rust with limited, known types?

I'm writing some code in Rust which can work equally as well with f64 and f32.

Attempting to convert this to use generic functions is quite involved; I need to declare the capabilities of the type on every use.

In practice, I only want f32 and f64 to be used. Is there a way to write generic functions that only apply to a set of known types?

I realize this goes against generic functions a little.

Upvotes: 7

Views: 1348

Answers (3)

paholg
paholg

Reputation: 2080

It might not be ideal, but I accomplish this by using a trait and a macro.

For example, say we want this function to take either f32 or f64:

fn sqr(a: f32) -> f32 {
    a * a
}

We can first put it in a trait:

trait Sqr {
    fn sqr(self) -> Self;
}

Then we make a macro for implementing it. The macro just takes a type, and implements the trait for that type:

macro_rules! impl_sqr {
    ($t:ty) => (
        impl Sqr for $t {
            fn sqr(self) -> Self {
                self * self
            }
        }
    );
}

Then we call the macro for f32 and f64:

impl_sqr!(f32);
impl_sqr!(f64);

Full example here.

Upvotes: 0

phaazon
phaazon

Reputation: 2002

Just use the Float trait from the num crate. You’ll have anything you need for both f32 and f64 only. Do not write your own trait if you don’t really need to.

Just add the crate num-traits into your [dependencies] section of your Cargo.toml file to be able to use that trait:

[dependencies]
# ...
num-traits = "0.1"

Upvotes: 4

Chris Emerson
Chris Emerson

Reputation: 14051

The only tool that comes to mind for this is a trait, so your function would be:

pub fn f<T: MyTrait>(x: T) {...}

You could then implement MyTrait for f32 and f64. This is close, but there's a hole: callers can implement the trait on their own types. You can't get around this by making MyTrait private, since Rust then complains that you're using a private type (MyTrait) in a public interface.

So close, but I don't think it's exactly possible!

Although I agree with the other answer about using an existing relevant trait, you would do it a bit like:

mod foo {
    pub trait MyTrait {}
    impl MyTrait for f32 {}
    impl MyTrait for f64 {}

    pub fn f<T: MyTrait>(x: T) {}
}

pub fn main() {
    foo::f(3f32);
    foo::f(3f64);
    // foo::f(3u64);  // doesn't work as u64 doesn't implement MyTrait
}

Upvotes: 3

Related Questions