1
struct Vec2<T: Sub<Output = T> + Add<Output = T> + Copy> {
    x: T,
    y: T
}

impl<T: Sub<Output = T> + Add<Output = T> + Copy> Add for Vec2<T> {
    type Output = Vec2<T>;
    fn add(self, rhs: Self) -> Self::Output {
        Vec2 {
            x: self.x + rhs.x,
            y: self.y + rhs.y
        }
    }
}

Do I need to rewrite this not-so-beautiful <T: Sub<Output = T> + Add<Output = T> + Copy> every time, or is there another, more elegant method that I am not familiar with?

I know one hack that can achive behaviour that I want by douing like so:

trait Numeric<T> : Sub<Output = T> + Add<Output = T> + Copy {}

macro_rules! add_impl {
    ($($t:ty)*) => ($(
        impl Numeric<$t> for $t {}
    )*)
}

add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }

#[derive(Debug)]
struct Vec2<T: Numeric<T>> {
    x: T,
    y: T
}

impl<T: Numeric<T>> Add for Vec2<T> {
    type Output = Vec2<T>;
    fn add(self, rhs: Self) -> Self::Output {
        Vec2 {
            x: self.x + rhs.x,
            y: self.y + rhs.y
        }
    }
}

This, while partially solving my problem, looks even messier in my opinion.

And I know that "you shoud not put trait bounds in the struct definition", but i really want the compiler to give an error if the end user will try to:

let v = Vec2 {
        x: "Hello", // ERROR WANTED HERE
        y: "World"
    };
  • You could make a new trait that requires the traits you want. See https://stackoverflow.com/questions/26070559/is-there-any-way-to-create-a-type-alias-for-multiple-traits – Andrew Sep 26 '22 at 15:51
  • 1
    What is the difference between having a compiler error when building `v` versus when doing an addition? It can be argued that the end result is the same. – SirDarius Sep 26 '22 at 16:12

1 Answers1

2

Trait aliases are unstable, but if you are using a nightly toolchain, you can alias your long trait bounds into a shorter one to actually specify in your impl blocks:

#![feature(trait_alias)]

use core::ops::{Add, Sub};

trait Numeric<T> = Sub<Output = T> + Add<Output = T> + Copy;

struct Vec2<T: Numeric<T>> {
    x: T,
    y: T
}

impl<T: Numeric<T>> Add for Vec2<T> {
    type Output = Vec2<T>;
    fn add(self, rhs: Self) -> Self::Output {
        Vec2 {
            x: self.x + rhs.x,
            y: self.y + rhs.y
        }
    }
}

A more normal method of implementing your second approach is to just impl Numeric for all types satisfying a trait bound, which removes the need for your macros:

impl<T> Numeric<T> for T where T: Sub<Output = T> + Add<Output = T> + Copy {}
Jeremy Meadows
  • 2,314
  • 1
  • 6
  • 22