3

We can restrict a type to one or more traits by where clause.

My questions are:

  • Is it possible to restrict a type to just primitive number types?

  • How?

Galaxy
  • 1,129
  • 11
  • 27

3 Answers3

2

No.

To use a parametric type, you need the trait to define the valid operations you want to call on it. So you need a trait (or more) with all the operations you want to call.

The “primitive” types in Rust are not special in any way. They define their operators via the traits from std::ops (though obviously using compiler intrinsics) just like any “non-primitive” numberic types.

In fact, the boundary between “primitive” and “non-primitive” numeric types is even somewhat blurred, since for targets that lack an FPU, the standard library may be implementing floating point types in code, and in Rust it can do it transparently to the user.

So really, there is no such thing as primitive number types. Number types are defined by providing whatever operators you need to call. So just restrict your type by the std::ops traits.

Jan Hudec
  • 73,652
  • 13
  • 125
  • 172
2

Since primitives are not trait types you cannot use as boundary to restrict a generic type. But you can directly implement for a specific type:

struct Struct<T>(T);

trait Printer {
    fn print(&self);
}

impl Printer for Struct<i32> {
    fn print(&self) {
        println!("Printing for i32 value: {}", self.0);
    }
}

fn main() {
    let x = Struct(15_i32);
    let _z = Struct(14.2_f64);

    x.print();
    //_z.print();//compile error
}

Playground

Alternatively you can use Borrow<S> trait as trick, you can restrict your generic parameter as like below : ( T: Borrow<S> means T can be borrowed as S ) .

impl<T> Printer for Struct<T>
where
    T: Borrow<f64> + Debug,
{
    fn print(&self) {
        println!("Printing for f64 value: {:?}", self.0);
    }
}

Playground

But since you can implement Borrow<f64> for any type, this kind of restriction may not be considered as strict.

Also just for the numeric primitives you can use traits from num-traits crate like ToPrimitive AsPrimitive

Ömer Erden
  • 7,680
  • 5
  • 36
  • 45
1

If you think about traits as compile time duck typing, then a better question would be: what are the exact traits you are looking for in a number? Most of the operations on them could be defined as trait constraints on your types, see: the standard operator traits.

Even if you would define a trait in an external crate and implement the behaviour for specific types only in that external crate, thinking that the trait implementation rules will help you there (i.e. a trait can only be implemented to a type if either the trait or the type or both are in your current crate) would still not limit anyone to implement your trait for their own types.

Therefore, I see no other option but to implement the behaviours without generics for each primitive number type. To avoid code duplication, I would probably use macros for this -- after all, if you think about it in some ways you would manually do what the compiler does while it monomorphises your generic code.

That being said I see no reason to limit behaviours to numbers but to certain traits and I would rely on them as I described it in the first paragraph.

Peter Varo
  • 11,726
  • 7
  • 55
  • 77