如何将任意大小的矩阵传递给Rust中的函数?

kos*_*nix 4 types matrix rust

我有一个传递给函数的3x3矩阵(2D数组):

let matrix: [[i32; 3]; 3] = [
    [0, 0, 0],
    [0, 1, 0],
    [0, 0, 0]
];

filter::convolve(&mut image, matrix, 1).unwrap();
Run Code Online (Sandbox Code Playgroud)

该功能目前硬连线接受3x3矩阵:

pub fn convolve(src: &mut Image, matrix: [[i32; 3]; 3], divisor: i32) -> Result<&mut Image, String> {
    // ...
}
Run Code Online (Sandbox Code Playgroud)

如何将3x3,5x5或任意大小的矩阵传递给同一个函数?

She*_*ter 8

数组具有固定大小,在编译时确定.切片具有固定的大小,在运行时确定.

最简单的方法是接受一片切片:

fn convolve(matrix: &[&[i32]]) {
    println!("{:?}", matrix);
}

fn main() {
    let matrix = &[
        &[0, 0, 0][..],
        &[0, 1, 0][..],
        &[0, 0, 0][..],
    ];
    convolve(matrix);
}
Run Code Online (Sandbox Code Playgroud)

这有点烦人,因为你必须使用切片语法(&foo[..])将文字数组转换为切片.您也可以接受通用,它允许您接受上述内容,但也可以接受任何可以转换为切片的内容:

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);
}
Run Code Online (Sandbox Code Playgroud)

正如kosinix指出的那样,不能保证&[&[i32]]会有相同长度的行; 它可能有一个粗糙的阵列.

对此的运行时解决方案是迭代所有行并确保所有长度都相同.这可以减少到如果你创建一个检查只是一次NEWTYPE为您确认矩阵:

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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,只要你有一个Matrix,你可以确定行的长度都是一样的.

编译时解决方案......尚不存在.这些被称为类型级整数(我确定其中许多其他名称).一个假设的语法就像是

fn convolve<const N: usize>(matrix: [[i32; N]; N]) 
Run Code Online (Sandbox Code Playgroud)

这是Rust社区想要的东西,但细节很复杂.有可用的解决方法(例如通用数组),但这些可能以某种方式受到限制.