-
-
Save ratijas/c0122a7ec276966513f8f6e92bd080d0 to your computer and use it in GitHub Desktop.
Using Box<[T]> in Rust for multi-dimensional arrays
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::{fmt, ops}; | |
pub struct Array2D<T> { | |
width: usize, | |
height: usize, | |
data: Box<[T]>, | |
} | |
impl<T> Array2D<T> { | |
pub fn new(size: (usize, usize)) -> Self | |
where T: Default | |
{ | |
let (width, height) = size; | |
assert_ne!(width, 0); | |
assert_ne!(height, 0); | |
let size = width.checked_mul(height).expect("Array of this size would not fit into memory"); | |
let mut vec = Vec::with_capacity(size); | |
vec.extend(std::iter::repeat_with(T::default).take(size)); | |
let data = vec.into_boxed_slice(); | |
Array2D { | |
width, | |
height, | |
data, | |
} | |
} | |
pub fn width(&self) -> usize { self.width } | |
pub fn height(&self) -> usize { self.height } | |
pub fn len(&self) -> usize { | |
// probably faster than multiplication | |
self.data.len() | |
} | |
fn make_index(&self, xy: (usize, usize)) -> usize { | |
let (x, y) = xy; | |
assert!(x < self.width); | |
assert!(y < self.height); | |
y * self.width + x | |
} | |
pub fn column(&self, column_index: usize) -> Column<T> { | |
assert!(column_index < self.width()); | |
Column::new(self, column_index) | |
} | |
pub fn columns<'a>(&'a self) -> impl Iterator<Item=Column<'a, T>> + 'a { | |
(0..self.width()).map(move |x| self.column(x)) | |
} | |
pub fn row(&self, row_index: usize) -> Row<T> { | |
assert!(row_index < self.height()); | |
Row::new(self, row_index) | |
} | |
pub fn rows<'a>(&'a self) -> impl Iterator<Item=Row<'a, T>> + 'a { | |
(0..self.height()).map(move |y| self.row(y)) | |
} | |
} | |
impl<T> ops::Index<(usize, usize)> for Array2D<T> { | |
type Output = T; | |
fn index(&self, xy: (usize, usize)) -> &T { | |
let index = self.make_index(xy); | |
&self.data[index] | |
} | |
} | |
impl<T> ops::IndexMut<(usize, usize)> for Array2D<T> { | |
fn index_mut(&mut self, xy: (usize, usize)) -> &mut T { | |
let index = self.make_index(xy); | |
&mut self.data[index] | |
} | |
} | |
impl<T> fmt::Display for Array2D<T> | |
where T: fmt::Display | |
{ | |
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
"Array2D([\n".fmt(f)?; | |
for row in self.rows() { | |
" [".fmt(f)?; | |
for (x, item) in row.enumerate() { | |
write!(f, "{}", item)?; | |
if x != (self.width() - 1) { | |
", ".fmt(f)?; | |
} | |
} | |
"]\n".fmt(f)?; | |
} | |
"])".fmt(f)?; | |
Ok(()) | |
} | |
} | |
pub struct Row<'a, T> { | |
array: &'a Array2D<T>, | |
row: usize, | |
current_column: usize, | |
} | |
impl<'a, T> Row<'a, T> { | |
pub fn new(array: &'a Array2D<T>, row: usize) -> Self { | |
assert!(row < array.height); | |
Row { | |
array, | |
row, | |
current_column: 0 | |
} | |
} | |
pub fn current_column(&self) -> usize { | |
self.current_column | |
} | |
} | |
impl<'a, T> Iterator for Row<'a, T> { | |
type Item = &'a T; | |
fn next(&mut self) -> Option<&'a T> { | |
if self.current_column >= self.array.width() { | |
None | |
} else { | |
let index = (self.current_column, self.row); | |
let item = &self.array[index]; | |
self.current_column += 1; | |
Some(item) | |
} | |
} | |
} | |
pub struct Column<'a, T> { | |
array: &'a Array2D<T>, | |
column: usize, | |
current_row: usize, | |
} | |
impl<'a, T> Column<'a, T> { | |
pub fn new(array: &'a Array2D<T>, column: usize) -> Self { | |
assert!(column < array.width); | |
Column { | |
array, | |
column, | |
current_row: 0 | |
} | |
} | |
pub fn current_row(&self) -> usize { | |
self.current_row | |
} | |
} | |
impl<'a, T> Iterator for Column<'a, T> { | |
type Item = &'a T; | |
fn next(&mut self) -> Option<&'a T> { | |
if self.current_row >= self.array.height() { | |
None | |
} else { | |
let index = (self.column, self.current_row); | |
let item = &self.array[index]; | |
self.current_row += 1; | |
Some(item) | |
} | |
} | |
} | |
fn incremental_array(size: (usize, usize)) -> Array2D<u8> { | |
let mut array = Array2D::new(size); | |
let mut item = 0u8; | |
for x in 0..array.width() { | |
for y in 0..array.height() { | |
array[(x, y)] = item; | |
item += 1; | |
} | |
} | |
array | |
} | |
fn main() { | |
let a = incremental_array((10, 15)); | |
println!("{}", a); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
rust playground permalink:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=01a32331d7a2dd71a0ad8d1babc0c170