I have some experience with Python and Go and now want to learn Rust from scratch. I am a self taught programmer with rather limited experience.
For the purpose of tackling some of the features of Rust I have decided to implement a Dataframe module in Rust. This is only intended for my educational purposes, not for distribution.
My main questions can be summarized
-
how/when to use generics
-
how/when to use traits
Here is my specific struggle:
I want to implement a struct
Series which contains the name and the data of the series. Given the data could be of different types (e.g. i32
, f32
, String
) I am using Vec<T>
.
struct Series<T> {
name: String,
elements: Vec<T>,
}
Let's say I want to implement a methods sum
and product
for Series
I am struggling how to implement this reasonably.
Given std::iter::Sum
and std:iter:Product
are not implemented for String
but are implemented for i32
and f32
I found the following solution:
-
define a
trait
IsNumeric
-
implement functions for
Series<i32>
andSeries<f32>
trait IsNumeric {
fn sum(&self) -> f32;
fn product(&self) -> f32;
}
impl IsNumeric for Series<i32> {
fn sum(&self) -> f32 {
let v = self.values.iter().sum::<i32>();
return v as f32
}
fn product(&self) -> f32 {
let v = self.values.iter().product::<i32>();
return v as f32
}
}
impl IsNumeric for Series<f32> {
fn sum(&self) -> f32 {
let v = self.values.iter().sum::<f32>();
return v
}
fn product(&self) -> f32 {
let v = self.values.iter().product::<f32>();
return v
}
}
This solution works, however, it doesn't feel right given I have to write the same functions twice. How would I use generics in this case.
Is this even the right approach? Or does it make sense to tackle this problem completley different.
I tried a somewhat different approach before using Enums
for Series.Elements
and then apply a match pattern. However,in the end I struggled with the same problem of having to implement a function for Elements::Int32
and Elements::Float32
.
#[derive(Debug)]
enum Elements {
Text(Vec<String>),
Int32(Vec<i32>),
Float32(Vec<f32>),
}
#[derive(Debug)]
struct Series {
name: String,
values: Elements,
}
impl Series {
pub fn new(name: &str, values: Elements) -> Series {
let name = name.to_string();
Series{
name,
values,
}
}
pub fn sum(&self) {
match &self.values {
Elements::Text(Vec) => println!("Text..."),
Elements::Int32(Vec) => println!("Integer"),
Elements::Float32(Vec) => println!("Number"),
// Note
// Elements::Int32(Vec) | Elements::Float32(Vec) ==> ...
// throws an error when compiling
}
}
}
I am very grateful for any help on how to approach this problem!
Thanks
Fred