Язык не автоматизирует это для вас.
Ваши варианты:
- Напишите реализации самостоятельно
- Создайте макрос и используйте метапрограммирование
zrzka был достаточно любезен, чтобы создать Rust Playground , который предоставляет примеры макросов для этого конкретного варианта использования.
Этот вопрос также содержит несколько полезных советов, поскольку сам исходный код Rust сам использует эти макросы для автоматизации некоторых утомительных действий.
Одна проблема, с которой я столкнулся, заключалась в том, что если бы я использовал макрос, мне пришлось бы называть его встроенным (например, vec![]
). Поскольку макросы раскрываются во время компиляции, ваш макрос будет генерировать для вас функции, которые вы можете вызывать обычным образом. RLS будет по-прежнему обеспечивать поддержку синтаксиса, и все будет работать так, как вы ожидаете.
Вот реализация, с которой я закончил. Я уверен, что гораздо больше можно автоматизировать (forward_ref_binop
для одного), но я доволен этим.
/// Generates the operations for vector methods. `let result = my_vec_3 + my_other_vec3`
/// Handles `Vec3, Vec3`, `Vec3, &Vec3`, `&Vec3, Vec3`, `&Vec3, &Vec3`
/// `vec3_vec3_op(ops::AddAssign, add_assign)` (note the camelcase add_assign name)
macro_rules! vec3_vec3_op {
($($path:ident)::+, $fn:ident) => {
impl $($path)::+<Vec3> for Vec3 {
type Output = Vec3;
fn $fn(self, other: Vec3) -> Self::Output {
Vec3 {
e0: self.e0.$fn(other.e0),
e1: self.e1.$fn(other.e1),
e2: self.e2.$fn(other.e2),
}
}
}
impl $($path)::+<&Vec3> for &Vec3 {
type Output = Vec3;
fn $fn(self, other: &Vec3) -> Self::Output {
Vec3 {
e0: self.e0.$fn(other.e0),
e1: self.e1.$fn(other.e1),
e2: self.e2.$fn(other.e2),
}
}
}
impl $($path)::+<&Vec3> for Vec3 {
type Output = Vec3;
fn $fn(self, other: &Vec3) -> Self::Output {
Vec3 {
e0: self.e0.$fn(other.e0),
e1: self.e1.$fn(other.e1),
e2: self.e2.$fn(other.e2),
}
}
}
impl $($path)::+<Vec3> for &Vec3 {
type Output = Vec3;
fn $fn(self, other: Vec3) -> Self::Output {
Vec3 {
e0: self.e0.$fn(other.e0),
e1: self.e1.$fn(other.e1),
e2: self.e2.$fn(other.e2),
}
}
}
};
}
/// Generates the operations for vector method assignment. `my_vec += my_other_vec`
/// Handles `Vec3, Vec3` and `Vec3, &Vec3`
/// `vec3_vec3_opassign(ops::AddAssign, add_assign)` (note the camelcase add_assign name)
macro_rules! vec3_vec3_opassign {
($($path:ident)::+, $fn:ident) => {
impl $($path)::+<Vec3> for Vec3 {
fn $fn(&mut self, other: Vec3) {
self.e0.$fn(other.e0);
self.e1.$fn(other.e1);
self.e2.$fn(other.e2);
}
}
impl $($path)::+<&Vec3> for Vec3 {
fn $fn(&mut self, other: &Vec3) {
self.e0.$fn(other.e0);
self.e1.$fn(other.e1);
self.e2.$fn(other.e2);
}
}
};
}
/// Generates the operations for method assignment. `my_vec += f32`
/// `vec3_opassign(ops:AddAssign, add_assign)` (note the camelcase add_assign name)
macro_rules! vec3_opassign {
($($path:ident)::+, $fn:ident, $ty:ty) => {
impl $($path)::+<$ty> for Vec3 {
fn $fn(&mut self, other: $ty) {
self.e0.$fn(other);
self.e1.$fn(other);
self.e2.$fn(other);
}
}
}
}
/// Generates the operations for the method. `let result = my_vec + 4f32`
/// Handles `Vec3, T`, `T, Vec3`, `&Vec3, T`, `T, &Vec3`
/// `vec3_op!(ops:Add, add, f32)`
macro_rules! vec3_op {
($($path:ident)::+, $fn:ident, $ty:ty) => {
// impl ops::Add::add for Vec3
impl $($path)::+<$ty> for Vec3 {
type Output = Vec3;
// fn add(self, other: f32) -> Self::Output
fn $fn(self, other: $ty) -> Self::Output {
Vec3 {
// e0: self.e0.add(other)
e0: self.e0.$fn(other),
e1: self.e1.$fn(other),
e2: self.e2.$fn(other),
}
}
}
impl $($path)::+<$ty> for &Vec3 {
type Output = Vec3;
fn $fn(self, other: $ty) -> Self::Output {
Vec3 {
e0: self.e0.$fn(other),
e1: self.e1.$fn(other),
e2: self.e2.$fn(other),
}
}
}
impl $($path)::+<Vec3> for $ty {
type Output = Vec3;
fn $fn(self, other: Vec3) -> Self::Output {
Vec3 {
e0: self.$fn(other.e0),
e1: self.$fn(other.e1),
e2: self.$fn(other.e2),
}
}
}
impl $($path)::+<&Vec3> for $ty {
type Output = Vec3;
fn $fn(self, other: &Vec3) -> Self::Output {
Vec3 {
e0: self.$fn(other.e0),
e1: self.$fn(other.e1),
e2: self.$fn(other.e2),
}
}
}
}
}
macro_rules! vec3_op_for {
($ty: ty) => {
vec3_op!(ops::Add, add, $ty);
vec3_op!(ops::Sub, sub, $ty);
vec3_op!(ops::Mul, mul, $ty);
vec3_op!(ops::Div, div, $ty);
vec3_opassign!(ops::AddAssign, add_assign, $ty);
vec3_opassign!(ops::SubAssign, sub_assign, $ty);
vec3_opassign!(ops::MulAssign, mul_assign, $ty);
vec3_opassign!(ops::DivAssign, div_assign, $ty);
};
}
vec3_vec3_op!(ops::Add, add);
vec3_vec3_op!(ops::Sub, sub);
vec3_vec3_op!(ops::Mul, mul);
vec3_vec3_op!(ops::Div, div);
vec3_vec3_opassign!(ops::AddAssign, add_assign);
vec3_vec3_opassign!(ops::SubAssign, sub_assign);
vec3_vec3_opassign!(ops::MulAssign, mul_assign);
vec3_vec3_opassign!(ops::DivAssign, div_assign);
vec3_op_for!(f32);
Отсюда, если бы я расширил свой класс Vec3
для обработки обобщенных типов, добавить дополнительные типы было бы тривиально.