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?
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));
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With