I have several different structures. These structures can have items of type Option<f64>
. I want with a single function to evaluate whether or not I keep values small (values < DELTA = 0.005).
Let KeysValuesA and KeysValuesB be structures defined with distinct numbers of items of type Option<f64>
:
#[derive(Debug, Default, PartialEq, PartialOrd, Clone)]
struct KeysValuesA {
key_a : String,
key_b : u32,
key_c : Option<u16>,
value_a: Option<f64>,
value_b: Option<f64>,
value_c: Option<f64>,
}
#[derive(Debug, Default, PartialEq, PartialOrd, Clone)]
struct KeysValuesB {
key_a : String,
key_b : Option<u16>,
value_a: Option<f64>,
value_b: Option<f64>,
value_c: Option<f64>,
value_d: Option<f64>,
}
After implementing the trait:
trait AllValues {
fn all_values_vec(&mut self) -> Vec<&mut Option<f64>>;
fn all_values_arr<const N: usize>(&mut self) -> [&mut Option<f64>; N];
}
impl AllValues for KeysValuesA {
fn all_values_vec(&mut self) -> Vec<&mut Option<f64>> {
let values: Vec<&mut Option<f64>> = vec![
&mut self.value_a,
&mut self.value_b,
&mut self.value_c,
];
values
}
fn all_values_arr<const N: usize>(&mut self) -> [&mut Option<f64>; N] { todo!() }
}
impl AllValues for KeysValuesB {
fn all_values_vec(&mut self) -> Vec<&mut Option<f64>> {
let values: Vec<&mut Option<f64>> = vec![
&mut self.value_a,
&mut self.value_b,
&mut self.value_c,
&mut self.value_d,
];
values
}
fn all_values_arr<const N: usize>(&mut self) -> [&mut Option<f64>; N] { todo!() }
}
I use the iterator for the function, like that (DELTA is a constant):
fn despise_small_values_vec<T: AllValues>(keysvalues: &mut T) {
for value in keysvalues.all_values_vec() {
if value.unwrap_or(0.0).abs() < DELTA {
*value = None;
}
}
}
So I can implement iterator with Output: Vec<&mut Option<f64>>
, but I cannot implement it with Output: array [&mut Option<f64>; N]
.
How to implement iterator for array [&mut Option<f64>; N]
such that N can vary according to the Struct used?
And what would be the advantage of using array instead of using vector?
Follow the codes: Rust Playground
CodePudding user response:
You can implement all_values_arr
using Vec
and then cast it to the array. Like this:
impl AllValues for KeysValuesB {
fn all_values_arr<const N: usize>(&mut self) -> [&mut Option<f64>; N] {
debug_assert_eq!(4, N);
vec![
&mut self.value_a,
&mut self.value_b,
&mut self.value_c,
&mut self.value_d,
].try_into().unwrap()
}
}
It compiles and works, but there is a huge inconvenience: for each all_values_arr
call, you need to specify the N
. Example:
keysvalues.all_values_arr::<4>(); // but you don't know exactly how many values keysvalues contain
Also, you can try to do it using associated constants but it also will not work because const generics require const context
and associated constants are not const.
So using the Vec
is the most convenience way in your situation.
CodePudding user response:
This is certainly some odd code... but I think you just need to make the trait itself generic over N
, not all_values_arr
. A generic argument means "the caller is free to choose any value of the generic that they see fit", which is not what you want; it's up to you, not the caller, how many items one of these structs has.
trait AllValues<const N: usize> {
fn all_values_arr(&mut self) -> [&mut Option<f64>; N];
fn all_values_vec(&mut self) -> Vec<&mut Option<f64>> {
self.all_values_arr().into()
}
}
impl AllValues<3> for KeysValuesA {
fn all_values_arr(&mut self) -> [&mut Option<f64>; 3] {
[&mut self.value_a, &mut self.value_b, &mut self.value_c]
}
}
impl AllValues<4> for KeysValuesB {
fn all_values_arr(&mut self) -> [&mut Option<f64>; 4] {
[
&mut self.value_a,
&mut self.value_b,
&mut self.value_c,
&mut self.value_d,
]
}
}