1

I want to migrate some code from C to Rust for learning purposes and to make my learning library a bit more multi-lingual.

The problem is that I know there's a way to integrate C libraries into Rust. That way I could use calloc in Rust to allow creating my array with a range specified at runtime.

But I don't want to use calloc here - I'd like to see the Rust way. But I really don't want to use vec! either; I had some stupid issues with it before so I don't want to use it just yet.

Here is the code:

pub struct Canvas {
    width: usize,
    height: usize,
    array: [char], // I want to declare its type but not its size yet
}

impl Canvas{
    pub fn new (&self, width: usize, height: usize) -> Canvas {
        Canvas {
            width: width,
            height: height,
            array: calloc(width, height), // alternative to calloc ?            }
    }
}

I hope my question is still idiomatic to the Rust way of code.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
xetra11
  • 7,671
  • 14
  • 84
  • 159

2 Answers2

5

First of all, I deeply suspect you don't want char; I'm assuming you want an "array of bytes", in which case you want u8.

Secondly, you can't really use [u8] like this. I'm not going to get into the why because that would just derail the answer. For now: if you see a [T] that isn't behind a reference or pointer of some kind, it's probably a mistake.

Finally, this is what Vec is for; use it. You say you don't want to use it, but don't specify why. Vec is how you allocate a dynamically sized array in Rust. If you're trying to allocate a structure that's compatible with the exact same structure in C, then the question changes quite a bit, and you should make that clear.

Assuming what you wanted was the "Rust equivalent" of doing it in C:

pub struct Canvas {
    width: usize,
    height: usize,
    array: Vec<u8>,
}

impl Canvas {
    pub fn new(width: usize, height: usize) -> Canvas {
        Canvas {
            width: width,
            height: height,
            array: vec![0; width*height],
        }
    }
}
Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
DK.
  • 55,277
  • 5
  • 189
  • 162
  • OK then I'll use vec! macro. But I don't come along with them very well. When I construct my canvas with a width of 10 and height of 10 the "array" binding will hold a vec with a length of 100 (index range 0-99). After using this API I want to access the 99th index of the array which I could to with array[99] - no problem so far. But when i want to access that array with coordinates style for a mutli-dimensional array I had to do array[9][9] which is not possible and array[9*9] will result in array[81] and not 9 in width and 9 in height...thats the issue I have here tbh – xetra11 Mar 16 '16 at 06:28
  • 1
    @xetra11 viewing a flat array as a 2d array can be done with trivial arithmetics (x + y*width, for example, in row-major order (that's how it is called I think)). – Vladimir Matveev Mar 16 '16 at 08:22
  • fix `pub fn new(&self, width: usize, height: usize)` to `pub fn new(width: usize, height: usize)` – qthree Mar 16 '16 at 09:07
5

i want to access that array with coordinates style

Something like this?

pub struct Canvas {
    width: usize,
    height: usize,
    data: Vec<u8>,
}

impl Canvas {
    pub fn new(width: usize, height: usize) -> Canvas {
        Canvas {
            width: width,
            height: height,
            data: vec![0; width*height],
        }
    }

    fn coords_to_index(&self, x: usize, y: usize) -> Result<usize, &'static str> {
        if x<self.width && y<self.height {
            Ok(x+y*self.width)
        } else {
            Err("Coordinates are out of bounds")
        }
    }

    pub fn get(&self, x: usize, y: usize) -> Result<u8, &'static str> {
        self.coords_to_index(x, y).map(|index| self.data[index])
    }

    pub fn set(&mut self, x: usize, y: usize, new_value: u8) -> Result<(), &'static str>{
        self.coords_to_index(x, y).map(|index| {self.data[index]=new_value;})
    }
}

fn main() {
    let mut canvas = Canvas::new(100, 100);
    println!("{:?}", canvas.get(50, 50)); // Ok(0)
    println!("{:?}", canvas.get(101, 50)); // Err("Coordinates are out of bounds")
    println!("{:?}", canvas.set(50, 50, 128)); // Ok(())
    println!("{:?}", canvas.set(101, 50, 128)); // Err("Coordinates are out of bounds")
    println!("{:?}", canvas.get(50, 50)); // Ok(128)
}
qthree
  • 236
  • 1
  • 6
  • Exactly what I was looking for !! – xetra11 Mar 16 '16 at 08:58
  • For a "built-in" feel, note the presence of the [Index](https://doc.rust-lang.org/std/ops/trait.Index.html) and [IndexMut](https://doc.rust-lang.org/std/ops/trait.IndexMut.html) which could implement for `(usize, usize)`. They cannot report errors, so they would come in supplement to the methods you wrote here, and then you could use `canvas[(50,50)] = 128`. – Matthieu M. Mar 16 '16 at 13:09