I have a case where the elements of a vector might be very large and I do not want to copy or clone them just to perform numerical operations.
Below is an example that demonstrates what I am trying to do:
[derive(Debug,Clone)]
struct ValVector {
vec : Vec<f32> ,
}
impl ValVector {
pub fn new( v : Vec<f32> ) -> Self {
Self{ vec : v }
}
}
impl std::ops::Add for ValVector {
type Output = Self;
fn add(self : Self, rhs : Self) -> Self {
let mut result = self.clone();
for i in 0 .. self.vec.len() {
result.vec[i] += rhs.vec[i];
}
result
}
}
fn main() {
//
let va = ValVector::new( vec![ 1f32, 2f32 ] );
let vb = ValVector::new( vec![ 3f32, 5f32 ] );
let vv = vec![va, vb];
let vc = vv[0] + vv[1];
//
println!("vc = {:?}", vc);
}
Here is the compiler error I get:
... snip ...
Compiling package v0.1.0 (/home/bradbell/trash/rust/package)
error[E0507]: cannot move out of index of `Vec<ValVector>`
--> src/main.rs:28:14
|
28 | let vc = vv[0] + vv[1];
| ^^^^^ move occurs because value has type `ValVector`, which does not implement the `Copy` trait
|
help: consider cloning the value if the performance cost is acceptable
|
28 | let vc = vv[0].clone() + vv[1];
|
..., snip ...
you can use a reference type for Rhs, but the only way for the + operator to not move the lhs (i.e. Self) is to implement Add for &ValVector instead of ValVector:
I don't see, either. The code produced by the source above is relatively well optimized as it is. vec.extend_from_slice(&rhs) would only lead to something more complicated.
Another, lighter option is to use += instead of + (compiled result). It's quite common with types that are "heavier". Obviously, it only works if you don't intend to use the (original) first operand later.
It's std so I think it's better. link
P/s: one is adding vec and one is extending vec. It's different result. So...
P/s: I think it is a extending at first
impl std::ops::Add<&ValVector> for &ValVector {
// ^^^^^^^^^^---+ The type underlined here
type Output = ValVector; // | has to match the overlined type below
// vvvv
fn add(self : Self, rhs : Self) -> ValVector {
...which is more obvious if you write out all the lifetimes.
impl<'a, 'b> std::ops::Add<&'a ValVector> for &'b ValVector {
type Output = ValVector;
// `Self` is `&'b ValVector`, not `&'a ValVector`
fn add(self : Self, rhs : Self) -> ValVector {
impl std::ops::Add<&ValVector> for &ValVector {
type Output = ValVector;
fn add(self : Self, rhs : &ValVector) -> ValVector {
(This implementation technically does more than the trait requires as the lifetime in rhs doesn't need to be related to the lifetimes in the header now, but that is okay -- similar to how you can leave off some where Self: Clone bound or the like on a trait method implementation, if you don't actually need the bound in the implementation yourself.)