'What is the difference between taking a slice and taking a reference to an array as argument?

What is the difference between taking a slice:

fn relu(xs: &mut [f64]) {
    xs.iter_mut().for_each(|x| *x = x.max(0.0));
}

and taking a reference to an array

fn relu<const L: usize>(xs: &mut [f64; L]) {
    xs.iter_mut().for_each(|x| *x = x.max(0.0));
}

as argument?

Does either have its uses or is one strictly better than the other?



Solution 1:[1]

Well, a slice is more general, since there are more types that can be used:

fn relu(xs: &mut [f64]) {
    xs.iter_mut().for_each(|x| *x = x.max(0.0));
}

You could call relu over a &mut Vec, &mut [], or &mut [_; N] (pretty much anything that you can make a slice from, remember that a slice is just a view over some continuous sequence), because those types can be used as slices.

Meanwhile:

fn relu<const L: usize>(xs: &mut [f64; L]) {
    xs.iter_mut().for_each(|x| *x = x.max(0.0));
}

Can only be use in a [f64; L], and a new function is generated for each L used, which in this case is pretty much a lose of resources .

As @Masklinn comments:

OTOH a reference to an array means the iteration size is definitely known at compile-time, which can allow optimisations which the slice version may not be able to work out. So sometimes using an array reference can make sense as an optimisation assistance (in this case relu would probably be a major candidate for inlining so the important bit would really be what the type is in the caller).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1