ys memos

Blog

Rust入門 Matrixクラス 3 演算子オーバーロード


rust

2022/09/24


Rust の勉強を目的とし,Matrix クラスを作る.

それを通して得た知見を残す.

コード全体はこちら



演算子オーバーロードを用いて Matrix クラスの要素へのアクセスを便利にする.


Rust では,演算子のために用意されたstd::ops内の Trait を Struct に実装することで演算子オーバーロードが可能である.

例えば+のためにはstd::ops::Addを,+=のためにはstd::ops::AddAssignを実装すると良い.

本ページでは,インデックスアクセスであるため,不変のIndexと,可変のIndexMutを実装する.


推奨されている方法というわけではないが,src/matrix/matrix.rsが肥大化するのを防ぐ目的で,別ファイルに分割した.

Indexsrc/matrix/index.rsに,IndexMutsrc/matrix/index_mut.rsに実装した.各テストについても同一フィアルに記載することにした.

src/matrix/index.rs
use std::ops::Index;

use super::Matrix;

impl<T> Index<u8> for Matrix<T> {
    type Output = [T];
    fn index(&self, index: u8) -> &Self::Output {
        let pos = (self.width as usize) * (index as usize);
        &self.data[pos..(pos + self.width as usize)]
    }
}

#[cfg(test)]
mod tests {
    use crate::matrix;

    #[test]
    fn get_row() {
        let mat = matrix![[0, 1, 2], [3, 4, 5], [6, 7, 8]];
        assert_eq!(mat[0], vec![0, 1, 2]);
        assert_eq!(mat[1], vec![3, 4, 5]);
        assert_eq!(mat[2], vec![6, 7, 8]);
        assert_eq!(mat, matrix![[0, 1, 2], [3, 4, 5], [6, 7, 8]]);
    }
}
src/matrix/index_mut.rs
use std::ops::IndexMut;

use super::Matrix;

impl<T> IndexMut<u8> for Matrix<T> {
    fn index_mut(&mut self, index: u8) -> &mut Self::Output {
        let pos = (self.width as usize) * (index as usize);
        &mut self.data[pos..(pos + self.width as usize)]
    }
}

#[cfg(test)]
mod tests {
    use crate::matrix;

    #[test]
    fn get_row() {
        let mut mat = matrix![[0, 1, 2], [3, 4, 5], [6, 7, 8]];
        mat[0][0] = 1;
        println!("{:?}", mat);
        assert_eq!(mat[0], vec![1, 1, 2]);
        assert_eq!(mat, matrix![[1, 1, 2], [3, 4, 5], [6, 7, 8]]);
    }
}

Matrix は行列でNxMの要素を持ち,それを一つのVec<T>として保持する.

一般に,行列の一つの要素を表す際は,nm列のような表現をする.

これをプログラムで表すと,mat[n][m]のようになっていると自然である.

Indexの実装でこのようなアクセスを可能にしたいので,[m]m個目の要素にアクセスできる共有スライスを返せば良い.

そこで,以下のように実装した.

use std::ops::Index;

use super::Matrix;

impl<T> Index<u8> for Matrix<T> {
    type Output = [T];
    fn index(&self, index: u8) -> &Self::Output {
        let pos = (self.width as usize) * (index as usize);
        &self.data[pos..(pos + self.width as usize)]
    }
}

#[cfg(test)]
mod tests {
    use crate::matrix;

    #[test]
    fn get_row() {
        let mat = matrix![[0, 1, 2], [3, 4, 5], [6, 7, 8]];
        assert_eq!(mat[0], vec![0, 1, 2]);
        assert_eq!(mat[1], vec![3, 4, 5]);
        assert_eq!(mat[2], vec![6, 7, 8]);
        assert_eq!(mat, matrix![[0, 1, 2], [3, 4, 5], [6, 7, 8]]);
    }
}


Indexへの実装と同様の考え方でスライスを返すが,これは可変スライスを返すようにしている.

これにより,mat[0][0] = 0のような式を書けるようになる.

use std::ops::IndexMut;

use super::Matrix;

impl<T> IndexMut<u8> for Matrix<T> {
    fn index_mut(&mut self, index: u8) -> &mut Self::Output {
        let pos = (self.width as usize) * (index as usize);
        &mut self.data[pos..(pos + self.width as usize)]
    }
}

#[cfg(test)]
mod tests {
    use crate::matrix;

    #[test]
    fn get_row() {
        let mut mat = matrix![[0, 1, 2], [3, 4, 5], [6, 7, 8]];
        mat[0][0] = 1;
        println!("{:?}", mat);
        assert_eq!(mat[0], vec![1, 1, 2]);
        assert_eq!(mat, matrix![[1, 1, 2], [3, 4, 5], [6, 7, 8]]);
    }
}

演算子オーバーロードを実装するために,特別な記法を用いる必要がない点が非常に便利であるし,Trait に対する実装ということで,実装への制限が与えられるのが安心感があって素晴らしいです.


関連タグを探す