Constructor for heterogeneous enum content

Hi everybody,
I'm setting up a library for some basic data science in Rust. I'm starting with some basic distance metrics.
I have a Structure Points to store two points and the features contained:

#[derive(Debug,Clone)]
struct Points<T>{
    point_base: Vec<T>,
    point_cfr: Vec<T>, 
    n_features: usize}

impl<T: std::fmt::Debug> Points<T>  {
    fn new(vbase:Vec<T>,vcfr:Vec<T>) -> Result<Points<T>,MetricError>  {
        let n_ft = vbase.len(); 
        if  n_ft == vcfr.len() {
            Ok(Points::<T>{point_base:vbase,point_cfr:vcfr,n_features:n_ft})
        } else {
            Err(MetricError::DifferetFeaturesNumber(vbase.len(),vcfr.len()))
        }
    }

    fn new_uncheck(vbase:Vec<T>,vcfr:Vec<T>,n_ft:usize) -> Result<Points<T>,MetricError>  {
        Ok(Points::<T>{point_base:vbase,point_cfr:vcfr,n_features:n_ft})
    }
}

The Points can be passed to the metric (an enum) in order to set the parameters of the metrics and check the type of input is coherent with the metric:

#[derive(Debug,Clone)]
enum Dist_Metric {
    Euclidean(Points<f64>),
    Manhattan(Points<f64>),
    Chebyshev(Points<f64>),
    Minkowski(Points<f64>,f64),
    StdEuclidean(Points<f64>,f64),

    Hamming(Points<isize>),
    Canberra(Points<isize>),
    Braycurtis(Points<isize>),

    Jaccard(Points<bool>),
}

impl Dist_Metric {
    fn calculate_distnce(metric:Dist_Metric) -> f64 {
        match metric {
            Dist_Metric::Euclidean(points) => {
                (0..points.n_features.clone()).map(|i| (points.vector_base[i]-points.vector_cfr[i]).powi(2)).sum::<f64>().sqrt()
            },
            Dist_Metric::Manhattan(points) => {
                (0..points.n_features.clone()).map(|i| (points.vector_base[i]-points.vector_cfr[i]).abs()).sum::<f64>()
            },
            Dist_Metric::Chebyshev(points) => {
                (0..points.n_features.clone()).map(|i| (points.vector_base[i]-points.vector_cfr[i]).abs()).fold(0., f64::max)
            },
            Dist_Metric::Minkowski(points,p) => {
                (0..points.n_features.clone()).map(|i| (points.vector_base[i]-points.vector_cfr[i]).powf(p)).sum::<f64>().powf(1./p)
            },
            Dist_Metric::StdEuclidean(points,v) => {
                (0..points.n_features.clone()).map(|i| ((points.vector_base[i]-points.vector_cfr[i]).powi(2))/v).sum::<f64>().sqrt()
            },
            Dist_Metric::Hamming(points) => {
                0.
            },
            Dist_Metric::Canberra(points) => {
                0.
            },
            Dist_Metric::Braycurtis(points) => {
                0.
            },
            Dist_Metric::Jaccard(points) => {
                0.
            },
        }
    }
}

Now I'm writing the constructor for the Dist_Metric enum. Is it possible the create a generic implementation of new to handle the creation or I should create a a new function for each variant (something like new_Euclidean, new_Manhattan, etc. )

Thanks,

In most cases, enums don't have constructors because all their variants and fields are always public. You should only write constructors if you are designing them to provide some specific benefit over using the enum variants directly.

3 Likes

Thanks for the response!
I was thinking to use constructor in order to generate a results, in order to raise an error if a wrong type was passed..

Unless you mean something other than Rust types, you can't check types in a constructor — the compiler checks the type, always, and a program with a type mismatch won't compile.

If you mean some non-type constraint like “the Points must have this many elements” then you need to enforce that in Points's constructor, not the enum's, because a caller can always construct the enum directly.

Thanks,
I think I will go with traits, creating distances implementation for specific types.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.