作者热门文章
- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
在 Rust 中,您可以重载运算符(+
、-
、/
、*
、+=
等)。我的 Vec3
有一个简单的添加实现输入:
use std::ops::Add;
struct Vec3 {
e0: f32,
e1: f32,
e2: f32,
}
impl Add<f32> for &Vec3 {
type Output = Vec3;
fn add(self, other: f32) -> Vec3 {
Vec3 {
e0: self.e0 + other,
e1: self.e1 + other,
e2: self.e2 + other,
}
}
}
我可以通过以下方式使用它:
let result = my_vec_3 + 43f32;
但是反过来会出错:
let this_wont_compile = 43f32 + my_vec_3;
error[E0277]: cannot add `Vec3` to `f32`
--> src/lib.rs:23:35
|
23 | let this_wont_compile = 43f32 + my_vec_3;
| ^ no implementation for `f32 + Vec3`
|
= help: the trait `std::ops::Add<Vec3>` is not implemented for `f32`
我知道我可以为 impl Add<&Vec3> for f32
编写一个实现但这正是我希望自动化的。
你如何编写你的实现使得 LHS
和 RHS
可以互换吗?在 Rust 中可能吗?
最佳答案
该语言不会为您自动执行此操作。
您的选择是:
zrzka 非常友好地构建了一个 Rust Playground为这个特定用例提供示例宏。
这question自 Rust Source Code 以来还提供了一些有用的提示本身使用这些宏来自动化一些乏味的事情。
我担心的一个问题是,如果我使用宏,我将不得不将其称为内联(如 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
类来处理泛型,那么添加额外的类型将是微不足道的。
关于rust - 如何在 Rust 中实现双向 LHS 和 RHS 运算符?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55266061/
我是一名优秀的程序员,十分优秀!