A simple maze generator written in Rust
Learn to love rustfmt.


Spaces go around operators and after commas

-    let c = make_odd((s.0/2, s.1/2));
+    let c = make_odd((s.0 / 2, s.1 / 2));


-    for (x,y,px) in buf.enumerate_pixels_mut() {
+    for (x, y, px) in buf.enumerate_pixels_mut() {

Don't put multiple statements on one line

-    o(&amp;mut t.0); o(&amp;mut t.1); t
+    o(&amp;mut t.0);
+    o(&amp;mut t.1);
+    t


Make a type for whatever (usize, usize) is. At the very least, create a type alias.
Actually, there are three places that want to apply a change to both components of the tuple. Promote the tuple to a type and add a map method.
If you have to give your closure arguments types anyway, and there's nothing captured by the closure, just make an inline function.
There's no real reason to take mutable reference to increment an integral value.
You define stack then immediately push a value on. instead, just use the vec! macro to create it all at once..
There's no need to specify the type of dirs
Instead of re-coding the number of elements in dir and having to potentially do bounds-checking again, just iterate over dirs directly
Characters are free. Use some longer variable names.
If you don't need the program name, skip it to avoid remembering it's there and then adding numbers here and there to compensate.
There's no need for the turbofish when parsing the maze size.
Use unwrap_or_else to handle a failure to parse.
Use enumerate to avoid indexing the args slice again.
Use std::cmp::max to provide a lower value for the sizes.
Don't ignore errors. If writing to stderr is important, use expect.
Use Vec::get instead of checking the size and then using the index operator (which checks the size again)
You don't need to contruct a Path, just pass in the &amp;str
Prefer expect to unwrap.
You can use ImageBuffer::from_fn. Note that this removes the need to make your variable mutable.
Dont' use ref in let bindings. Using &amp; on the right hand side is more idiomatic. In this case, &amp;mut at call is more idiomatic.
To deal with the unsigned / signed transformations, remember that a + (-2) is the same as a - (+2). Add an enum to define the directions, a method to apply those directions to a point, use checked_add / checked_sub in that method to deal with under/overflow, then add your own "overflow" check for the size of the maze.




extern crate rand;
extern crate image;

use rand::Rng;
use std::cmp::max;
use std::io::Write;

#[derive(Clone, PartialEq)]
enum Cell {
    Blocked,
    Free,
}

#[derive(Debug, Copy, Clone)]
enum Direction {
    Up,
    Down,
    Left,
    Right,
}

#[derive(Debug, Copy, Clone, PartialEq)]
struct Point(usize, usize);

impl Point {
    fn map&lt;F&gt;(self, mut f: F) -&gt; Self
        where F: FnMut(usize) -&gt; usize
    {
        Point(f(self.0), f(self.1))
    }

    fn move_by(self, amount: usize, dir: Direction) -&gt; Option&lt;Self&gt; {
        use Direction::*;
        match dir {
            Left  =&gt; self.0.checked_sub(amount).map(|x| Point(x, self.1)),
            Right =&gt; self.0.checked_add(amount).map(|x| Point(x, self.1)),
            Up    =&gt; self.1.checked_sub(amount).map(|y| Point(self.0, y)),
            Down  =&gt; self.1.checked_add(amount).map(|y| Point(self.0, y)),
        }
    }
}

fn next_odd_number(n: usize) -&gt; usize {
    n + if n % 2 == 0 { 1 } else { 0 }
}

fn generate_maze(size: Point) -&gt; Vec&lt;Vec&lt;Cell&gt;&gt; {
    use Direction::*;

    let center = size.map(|v| v / 2).map(next_odd_number);
    let mut stack = vec![Point(center.0, center.1)];

    let mut maze = vec![vec![Cell::Blocked; size.1]; size.0];
    maze[center.0][center.1] = Cell::Free;

    let mut dirs = [Left, Right, Up, Down];
    let mut rng = rand::thread_rng();

    let ensure_in_bounds = |z: Point| {
        if z.0 &gt;= size.0 || z.1 &gt;= size.1 {
            None
        } else {
            Some(z)
        }
    };

    'next_odd_number: while let Some(&amp;point) = stack.last() {
        rng.shuffle(&amp;mut dirs);

        for &amp;dir in &amp;dirs {
            let new_point = match point.move_by(2, dir).and_then(&amp;ensure_in_bounds) {
                Some(new_point) =&gt; new_point,
                None =&gt; continue,
            };

            if maze[new_point.0][new_point.1] != Cell::Free {
                stack.push(new_point);
                maze[new_point.0][new_point.1] = Cell::Free;
                let middle_point = point.move_by(1, dir)
                    .expect("Middle point cannot be out-of-bounds");
                maze[middle_point.0][middle_point.1] = Cell::Free;
                continue 'next_odd_number;
            }
        }
        stack.pop();
    }

    maze[0][1] = Cell::Free;
    maze[size.0 - 1][size.1 - 2] = Cell::Free;
    maze
}

fn print_usage() -&gt; ! {
    writeln!(std::io::stderr(), "Usage: maze-gen width height [output.png]")
        .expect("Unable to write to stderr");
    std::process::exit(1);
}

fn main() {
    let args: Vec&lt;_&gt; = std::env::args().skip(1).collect();
    if args.len() &lt; 2 {
        print_usage();
    }

    let mut dimensions = [0; 2];
    for (i, arg) in args.iter().enumerate() {
        dimensions[i] = arg.parse().unwrap_or_else(|_| print_usage());
    }
    let requested_size = Point(dimensions[0], dimensions[1]);
    let valid_size = requested_size.map(next_odd_number).map(|v| max(v, 3));

    if valid_size != requested_size {
        writeln!(std::io::stderr(),
                 "Warning: Adjusting sizes to {} and {}!",
                 valid_size.0,
                 valid_size.1)
            .expect("Unable to write to stderr");
    }

    let maze = generate_maze(valid_size);

    let buf = image::ImageBuffer::from_fn(valid_size.0 as u32, valid_size.1 as u32, |x, y| {
        let luma = match maze[x as usize][y as usize] {
            Cell::Free =&gt; 255,
            Cell::Blocked =&gt; 0,
        };
        image::Luma([luma])
    });
    let filename = args.get(2).map(String::as_str).unwrap_or("out.png");

    let mut file = std::fs::File::create(filename).expect("Couldn't open the file");
    image::ImageLuma8(buf).save(&amp;mut file, image::PNG).expect("Coulding write the file");
}


I might next look into implementing a flat storage for the maze, one that can be directly indexed by a Point. That would allow this change:

-maze[middle_point.0][middle_point.1] = Cell::Free;
+maze[middle_point] = Cell::Free;


I'll be honest and say that I strongly dislike the usage of the labelled loop. I don't think I've ever needed them, and they are one of the rarest constructs in Rust, based on my experience. I'd probably spend a lot of time (more than I already did...) trying to rewrite that in a different manner.