3

Is it possible with to constrain a generic parameter to be one of the select few types without figuring out what traits precisely define those type? e.g.

impl<T> Data<T> where T == u32 || T == u64 

Sometimes it's tedious to figure out what all traits to add to where to get the types you want, and sometimes one wouldn't want to allow a type even when it makes syntactic sense because of semantics.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204
divbyzero
  • 313
  • 1
  • 11
  • *Sometimes it's tedious to figure out what all traits to add to where to get the types you want* — sometimes it's tedious to get code that explicitly states its dependencies and doesn't mysteriously fail when some arbitrary type somewhere decides to rename a method; and I consider that a **good thing**. Rust is not Go. Automatically inferring membership in a trait is not a good thing, as the *signature* of a function is not the only thing that makes up the contract inferred by a trait. There are aspects that cannot be expressed in code, and that's why a human has to apply a trait. – Shepmaster Feb 22 '17 at 16:04
  • I agree. My particular use case here is not one of those times, because it make one have to look deeper under the hood than a high level use case should require in theory (i.e. a numeric trait for integers without using an external crate), for example, `1..n` requires a trait, the type signature for a `std::ops::Add` that can support `a + b + c` starts to go deeper, and then you realize trait for `Zero` is not in stable. FWIW i share the sentiment on `go`. – divbyzero Feb 23 '17 at 08:42

2 Answers2

2

You could use a marker trait for the types you want to support:

trait DataSupported {}

impl DataSupported for u64 {}
impl DataSupported for u32 {}

impl<T> Data<T> where T: DataSupported {}

As Pavel Strakhov mentioned, if you need to use this trait for a few impls and you need other trait bounds, then you can just make those traits as bounds of your marker trait instead, which will keep your impls terse:

trait DataSupported: Num + Debug {}
Community
  • 1
  • 1
Peter Hall
  • 53,120
  • 14
  • 139
  • 204
  • Actually this causes problems as the compiler doesn't infer the common set of traits implemented by these types, and so you have to put each bound individually – divbyzero Feb 22 '17 at 05:09
  • 1
    You can do something like `trait DataSupported : Display + Debug {}` and write all traits you need there. You won't have to list those traits in `impl`s. – Pavel Strakhov Feb 22 '17 at 13:17
  • @PavelStrakhov thanks, I'll update my answer with that. – Peter Hall Feb 22 '17 at 17:00
0

You can use a macro to add implementations based on a list of types:

macro_rules! data_impl {
    ($($t: ty),+) => {
        $(
            impl Data<$t> {
                // methods go here
            }
        )+
    }
}

data_impl!(u32, u64, i32, i64);

It's probably worth adding #[allow(dead_code)] to each method to prevent warnings, since you'll be defining lots of specialised methods that may not be used in your application if you don't use them for every possible type. This is exactly the same thing that the compiler does anyway if you define methods over a parameter T (a process called monomorphisation). The difference is that the macro-generated methods are defined during the compiler's parse step (before linting) while monomorphisation happens later in the pipeline.

Peter Hall
  • 53,120
  • 14
  • 139
  • 204