0

I am using the generic_array crate in Rust. I want fixed length arrays, so Vec is not appropriate, but I want the length of the array to be defined at runtime, hence using this crate instead of array.

The code is for a clustering algorithm, where each generic array represents a data point. I want the data to be of any dimension. For example, if I was clustering pixels on an image, I might have 3D data, one for each colour channel; but I don't want the code to only be able to be used on 3D data.

I'm having issues with reading data from a CSV file into a generic array. Before using generic arrays, I had a 2D struct that derived from RustcDecodable.

How best to implement reading data from a file into a GenericArray? I can't implement the Decodable trait for GenericArray, as the Decodable trait is external.

Here is what my code looked like when I was only allowing 2D data:

use std::path::Path;

extern crate csv;
extern crate rustc_serialize;

/// Store one data point's (or one cluster centroid's) x and y co-ordinates
#[derive(Clone, Copy, Debug, RustcDecodable)]
pub struct DataPoint {
    pub x: f64,
    pub y: f64,
}

impl DataPoint {

    pub fn squared_euclidean_distance(&self, other: &DataPoint) -> f64 {
        (other.x - self.x).powi(2) + (other.y - self.y).powi(2)
    }
}

pub fn read_data<P>(file_path: P) -> Vec<DataPoint>
    where P: AsRef<Path>
{
    let mut reader = csv::Reader::from_file(file_path).unwrap();
    reader.decode().map(|point| point.unwrap()).collect()
}

Here is what my code looks like with GenericArrays (minus read_data function):

extern crate generic_array;

trait Euclidean<N> {
    fn squared_euclidean_distance(&self, other: &generic_array::GenericArray<f64, N>) -> f64
        where N: generic_array::ArrayLength<f64>;
}


impl <N> Euclidean<N> for generic_array::GenericArray<f64, N>
    where N: generic_array::ArrayLength<f64>
{
    fn squared_euclidean_distance(&self, other: &generic_array::GenericArray<f64, N>) -> f64
        where N: generic_array::ArrayLength<f64>
    {
        let iter = self.iter().zip(other.iter());
        iter.fold(0.0, |acc, x| acc + (x.0 - x.1).powi(2))
    }
}

Should I read the data into something intermediate, then into the GenericArray? Should I define my own version of the Decodable trait? Should I give up on generic arrays?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
lochsh
  • 366
  • 1
  • 12
  • 2
    Why is using `Vec` not appropriate? Just because it *can* change size does not mean it *has* to. – Matthieu M. Oct 19 '16 at 12:32
  • 1
    You could use a NewType like explained here: http://valve.github.io/blog/2014/08/26/json-serialization-in-rust-part-2/ – Neikos Oct 19 '16 at 12:35
  • 1
    @MatthieuM. I want a data structure that can't change size -- using one that _could_ leaves room for bugs and doesn't feel like a nice choice of data structure. I'd rather use a type that guarantees the behaviour I want. – lochsh Oct 19 '16 at 12:35
  • @Neikos do you mean creating a tuple struct containing the generic array, and deriving `RustcDecodable` on that? I had previously tried this (sorry for not including in post), and get the following error: `error[E0277]: the trait bound \`generic_array::GenericArray: rustc_serialize::Decodable\` is not satisfied` – lochsh Oct 19 '16 at 12:43
  • 2
    A `Vec`-like structure with a fixed size? How about a `Box<[T]>`? – ljedrz Oct 19 '16 at 12:43
  • 1
    @lochsh Yes, it would contain it, but then you can implement the Decodable trait manually check the "Deserializing fixed length arrays" part in the post I linked – Neikos Oct 19 '16 at 12:58
  • @Neikos thanks! I will look tomorrow more closely (busy atm), it sounds very promising :) – lochsh Oct 19 '16 at 13:13
  • 2
    If you use a newtype, please mark this question a duplicate of http://stackoverflow.com/q/25413201/155423. – Shepmaster Oct 19 '16 at 13:16
  • Newtype did indeed solve my problem. Thank you very much @Neikos. I will mark as duplicate :) – lochsh Oct 19 '16 at 22:35

0 Answers0