Some explanation as to what I am trying to do here. The full details of the calculation are not particularly important (and also cannot be made public).
However, the following points should be noted:
There are a set of operations which may be performed for any particular type T which is used in the calculation.
T may be a floating point value, or an ndarray of floating point values:
- float += float
- float *= float
- float + float
- float * float
- ndarray += float
- ndarray += ndarray
- ndarray *= float
- ndarray + float
- ndarray + ndarray
- ndarray * float
To be more specific, ndarray does not support += unless the right hand side is a reference. So the += operators, should be AddAssign<&T> not AddAssign<T>.
I am trying to implement this as a trait for a particular type in Rust, but I cannot get the code to compile, because my expression of a reference lifetime is not valid. Because this trait takes a reference, the compiler encouraged me to introduce a lifetime. But I think I am associating that lifetime incorrectly.
Here is a link to the playground.
use ndarray; // 0.15.6
trait ExampleTrait {
type Input;
type Output;
fn do_calculation(&mut self, input: Self::Input) -> Self::Output;
}
struct Transform<T> {
current_state: Option<T>,
scale_factor: f64,
}
impl<T> Transform<T> {
fn new(scale_factor: f64) -> Transform<T> {
Transform::<T> {
current_state: None,
scale_factor,
}
}
}
impl<'a, T> ExampleTrait for Transform<T>
where
T: 'a
+ Clone
+ std::ops::AddAssign<f64>
+ std::ops::AddAssign<&'a T>
+ std::ops::MulAssign<f64>
+ std::ops::Add<T, Output = T>
+ std::ops::Add<f64, Output = T>
+ std::ops::Mul<f64, Output = T>,
{
type Input = T;
type Output = T;
fn do_calculation(&mut self, input: Self::Input) -> Self::Output {
let intermediate = input;
let current_state = std::mem::take(&mut self.current_state);
match current_state {
Some(mut state) => {
state *= self.scale_factor;
state += &intermediate;
let mut return_value = state.clone();
return_value *= 1.0;
return return_value;
}
None => {
self.current_state = Some(intermediate.clone());
return intermediate;
}
}
}
}
fn main() {
let tmp = 1.0;
let input = ndarray::array![[tmp, tmp, tmp], [tmp, tmp, tmp]];
let mut transform = Transform::<f64>::new(0.5);
transform.do_calculation(50.0);
let mut transform2 = Transform::<ndarray::Array2<f64>>::new(0.5);
transform2.do_calculation(input.clone());
}
This is the error produced:
Compiling playground v0.0.1 (/playground)
error[E0597]: `intermediate` does not live long enough
--> src/main.rs:63:26
|
37 | impl<'a, T> ExampleTrait for Transform<T>
| -- lifetime `'a` defined here
...
55 | let intermediate = input;
| ------------ binding `intermediate` declared here
...
63 | state += &intermediate;
| ---------^^^^^^^^^^^^^
| | |
| | borrowed value does not live long enough
| requires that `intermediate` is borrowed for `'a`
...
75 | }
| - `intermediate` dropped here while still borrowed
For more information about this error, try `rustc --explain E0597`.
error: could not compile `playground` (bin "playground") due to previous error
Btw I'm aware there's a bug due to not putting back the object taken by std::mem::take.