为Vector类型有效实现Add trait的首选方法是什么

时间:2015-02-02 17:47:58

标签: rust

Add特征定义为seen in the documentation

为Vector实现它时,需要将其复制到add方法中以允许v1 + v2之类的语法。如果更改add实现以支持借用的引用并因此阻止副本,则必须编写&v1 + &v2这是不合需要的。

这样做的首选或最佳效果是什么?

(在C ++中,self将是const Vector<T>&,以及rhs,但仍然允许所需的v1 + v2语义。)

守则

为了完整性,我现在正在使用的代码摘录

use std::num::Float;
use std::ops::Add;

#[derive(Debug, PartialEq, Eq, Copy)]
pub struct Vector<T: Float> {
    x: T,
    y: T,
    z: T,
}

impl<T: Float> Add for Vector<T> {
    type Output = Vector<T>;

    // Probably it will be optimized to not actually copy self and rhs for each call !
    #[inline(always)]
    fn add(self, rhs: Vector<T>) -> Vector<T> {
      Vector {  x: self.x + rhs.x,
                y: self.y + rhs.y, 
                z: self.z + rhs.z }
    }
}


#[cfg(test)]
#[test]
fn basics() {
    let v32 = Vector { x: 5.0f32, y: 4.0f32, z: 0.0f32 };
    let v32_2 = v32 + v32;
    assert_eq!(v32_2.x, v32.x + v32.x);
    assert_eq!(v32_2.y, v32.y + v32.y);
    assert_eq!(v32_2.z, v32.z + v32.z);
}

1 个答案:

答案 0 :(得分:4)

由于您的Vector仅包含三个实现Float特征的值(这意味着它们是f64f32),因此您不应该真正打扰它们被复制除非您已对程序进行了分析并确定多个副本导致性能下降。

如果你的类型不是可复制的并且在构造上需要分配(例如大整数和大浮点数),你可以实现按值和按引用调用的所有可能组合:

impl Add<YourType> for YourType { ... }
impl<'r> Add<YourType> for &'r YourType { ... }
impl<'a> Add<&'a YourType> for YourType { ... }
impl<'r, 'a> Add<&'a YourType> for &'r YourType { ... }

并在按值接受至少一个参数的实现中重用已分配的存储。但是,在这种情况下,如果您不想将值移动到调用中,需要使用&运算符。 Rust更倾向于隐式;如果你需要引用语义,你必须明确地写它。

FWIW,您可以查看this程序,尤其是其程序集输出。我认为,这件装配工作负责所有算术运算:

shrq    $11, %r14
cvtsi2sdq   %r14, %xmm0
mulsd   .LCPI0_0(%rip), %xmm0
shrq    $11, %r15
cvtsi2sdq   %r15, %xmm1
mulsd   .LCPI0_0(%rip), %xmm1
shrq    $11, %rbx
cvtsi2sdq   %rbx, %xmm2
mulsd   .LCPI0_0(%rip), %xmm2
movaps  %xmm0, %xmm3
addsd   %xmm1, %xmm3
movaps  %xmm1, %xmm4
addsd   %xmm2, %xmm4
movaps  %xmm0, %xmm5
addsd   %xmm2, %xmm5
addsd   %xmm2, %xmm3
addsd   %xmm0, %xmm4
addsd   %xmm1, %xmm5
movsd   %xmm3, 24(%rsp)
movsd   %xmm4, 32(%rsp)
movsd   %xmm5, 40(%rsp)
leaq    (%rsp), %rdi
leaq    24(%rsp), %rsi
callq   _ZN13Vec3$LT$T$GT$9to_string20h7039822990634233867E

看起来很整洁 - 编译器已经很好地内联了所有操作。