2

I would like to implement a function in rust, computing the norm of an array or Vec

for an Vec<f64> I would write the function as

pub fn vector_norm( vec_a : &Vec<f64> ) -> f64 { 
                                             
    let mut norm = 0 as f64;                     
    for i in 0..vec_a.len(){                     
        norm  +=  vec_a[i] * vec_a[i];     
    }                                  
    norm.sqrt()
}                                             

and for an &[f64] I would do

    pub fn vector_norm( vec_a : &[f64] ) -> f64 { 
                                             
    let mut norm = 0 as f64;                     
    for i in 0..vec_a.len(){                     
        norm  +=  vec_a[i] * vec_a[i];     
    }                                  
    norm.sqrt()
}    

But is there a way to combine both versions into a single function by the use of traits. I was thinking of something like

pub fn vector_norm<T:std::iter::ExactSizeIterator> 
                ( vec_a : &T ) -> f64 {        
                                             
let mut norm = 0 as f64;                       
for i in 0..vec_a.len(){               
    norm  +=  vec_a[i] * vec_a[i]; 
}
norm.sqrt()                               

}

This does not work because the the template parameter T is not indexable. Is it possible to do this somehow?? Maybe with an iterator trait or something?

zodiac
  • 291
  • 2
  • 15
  • 8
    The `&[f64]` one will also work for `&Vec` due to the `Deref` impl on `Vec` – Tavian Barnes Nov 05 '21 at 19:36
  • Related: [why shouldn't you take in an `&Vec`](https://stackoverflow.com/questions/40006219/why-is-it-discouraged-to-accept-a-reference-to-a-string-string-vec-vec-o) – Aiden4 Nov 06 '21 at 04:37

2 Answers2

5

First of all, Vec<T> implements Deref for [T]. This means that &Vec<f64> can be implicitly converted into &[f64]. So, just taking in a &[f64] will work:

fn vector_norm(vec_a: &[f64]) -> f64 {
    let mut norm = 0 as f64;
    for i in 0..vec_a.len() {
        norm += vec_a[i] * vec_a[i];
    }
    norm.sqrt()
}

fn main() {
    let my_vec = vec![1.0, 2.0, 3.0];
    // &my_vec is implicitly converted to &[f64]
    println!("{:?}", vector_norm(&my_vec));
}

However, if you want to broaden the acceptable values even further to all slice-like types, perhaps AsRef may be of use:

fn vector_norm<T: AsRef<[f64]>>(vec_a: T) -> f64 {
    // use AsRef to get a &[f64]
    let vec_a: &[f64] = vec_a.as_ref();
    let mut norm = 0 as f64;
    for i in 0..vec_a.len() {
        norm += vec_a[i] * vec_a[i];
    }
    norm.sqrt()
}

fn main() {
    let my_vec = vec![1.0, 2.0, 3.0];
    println!("{:?}", vector_norm(&my_vec));
}
Aplet123
  • 33,825
  • 1
  • 29
  • 55
3

In addition to Aplet's answer, I'd add that if you're taking something that is only going to be used in a for _ in loop, you might want to look at IntoIterator.

fn vector_norm<T: IntoIterator<Item = f64>>(t: T) -> f64 {
  let mut norm = 0f64;
  for i in t {
    norm += i * i;
  }
  norm.sqrt()
}

When you write for i in t, the compiler rewrites that into something that looks a bit more like this:

let mut iter = t.into_iter();
loop {
  match iter.next() {
    None => break,
    Some(i) => {
      // loop body
    }
  }
}

So if you only want to constrain your input as "something that works in a for loop", IntoIterator is the trait you're looking for.

cameron1024
  • 9,083
  • 2
  • 16
  • 36