I have a 3x3 matrix (a 2D array) passed to a function:
let matrix: [[i32; 3]; 3] = [
[0, 0, 0],
[0, 1, 0],
[0, 0, 0]
];
filter::convolve(&mut image, matrix, 1).unwrap();
The function is currently hardwired to accept 3x3 matrix:
pub fn convolve(src: &mut Image, matrix: [[i32; 3]; 3], divisor: i32) -> Result<&mut Image, String> {
// ...
}
How would I pass a 3x3, 5x5, or any arbitrarily sized matrix to the same function?
Arrays have a fixed size, determined at compile time. Slices have a fixed size, determined at run-time.
The easiest thing to do is to accept a slice of slices:
fn convolve(matrix: &[&[i32]]) {
println!("{:?}", matrix);
}
fn main() {
let matrix = &[
&[0, 0, 0][..],
&[0, 1, 0][..],
&[0, 0, 0][..],
];
convolve(matrix);
}
That's a bit annoying, as you have to use the slicing syntax (&foo[..]
) to convert the literal arrays to slices. You could also accept a generic, which allows you to accept the above, but also anything that can be converted to a slice:
fn convolve<T, I>(matrix: &[T])
where
T: AsRef<[I]>,
I: std::fmt::Debug,
{
for part in matrix {
println!("{:?}", part.as_ref());
}
}
fn main() {
let matrix = &[
[0, 0, 0],
[0, 1, 0],
[0, 0, 0],
];
convolve(matrix);
}
As kosinix points out, there is no guarantee that &[&[i32]]
will have rows of equal lengths; it's possible to have a ragged array.
The run-time solution to that is to iterate through all the rows and ensure all the lengths are the same. This can be reduced to checking just once if you create a newtype for matrices you have validated:
struct Matrix<'a, T: 'a>(&'a [&'a [T]]);
impl<'a, T> Matrix<'a, T> {
fn new(slice: &'a [&'a [T]]) -> Result<Self, ()> {
if slice.is_empty() {
return Ok(Matrix(slice));
}
let (head, tail) = slice.split_at(1);
let expected = head[0].len();
if tail.iter().map(|row| row.len()).all(|l| l == expected) {
Ok(Matrix(slice))
} else {
Err(()) // return a real error here
}
}
}
Now, whenever you have a Matrix
, you can be sure the rows are all the same length.
The compile-time solution... doesn't exist yet. These are called const generics. The proposed syntax would be
fn convolve<const N: usize>(matrix: [[i32; N]; N])
There are stable workarounds available (such as generic-array), but these may be limited in one fashion or another.
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