Evaluating a tic-tac-toe board using a C-style for loop in Rust I program in C, and it's my favourite language. I've recently found Rust. I like their ideas. Sometimes looks like a weird mixture of C++ and Haskell, but overall it looks nice.

The for loop in C is very flexible, and I truly miss them in Rust. Just look at my messy beginner Rust code below. Below that there is the equivalent C version, which (hopefully) doesn't look 'that' bad. I put the C version because I know my Rust code is unreadable; you need to look at C to understand why I wrote that mess. Tell me how to make my Rust code better. It doesn't have to copy all the C patterns, but there really needs to be something to compensate the lack of the flexible for loop.

If you're interested in what the whatever my code does. It determines the result of a tic-tac-toe board.

#![feature(libc)]

extern crate libc;

const SIZE: usize = 3;
const SIZE_2: usize = SIZE * SIZE;

fn determine_result(board: &amp;[i32; SIZE_2]) -&gt; i32 {
  'out: loop {
    let mut i = 0;
    'a: while i &lt; SIZE_2 {
      if board[i] == 0 {
        i += SIZE;
        continue;
      }
      for j in i + 1 .. i + SIZE {
        if board[i] != board[j] {
          i += SIZE;
          continue 'a;
        }
      }
      return board[i];
    }
    'b: for i in 0 .. SIZE {
      if board[i] == 0 {
        continue;
      }
      let mut j = i + SIZE;
      while j &lt; i + SIZE_2 {
        if board[i] != board[j] {
          continue 'b;
        }
        j += SIZE;
      }
      return board[i];
    }
    i = SIZE + 1;
    while i &lt; SIZE_2 {
      if board[i] != board[0] {
        break 'out;
      }
      i += SIZE + 1;
    }
    return board[0];
  }
  let mut i = SIZE * 2 - 2;
  while i &lt;= SIZE_2 - SIZE {
    if board[i] != board[SIZE - 1] {
      return 0;
    }
    i += SIZE - 1;
  }
  return board[SIZE - 1];
}

const N: usize = 50000000;

fn main() {
  unsafe {
    libc::srand(0);
  }
  let mut n = 0 as usize;
  let mut board: [i32; SIZE_2] = [0; SIZE_2];
  for i in 0 .. N {
    for i in 0 .. SIZE_2 {
      unsafe {
        board[i] = libc::rand() as i32 % 3;
      }
    }
    n += determine_result(&amp;board) as usize;
  }
  println!("{}", n);
}




#include &lt;stdio.h&gt;
#include &lt;stdlib.h&gt;

#define SIZE 3
#define SIZE_2 (SIZE * SIZE)

static int determineResult(int board[static SIZE_2]) {
  for (int i = 0; i &lt; SIZE_2; i += SIZE) {
    if (!board[i]) {
      continue;
    }
    for (int j = i + 1; j &lt; i + SIZE; ++j) {
      if (board[i] != board[j]) {
        goto next;
      }
    }
    return board[i];
  next:;
  }
  for (int i = 0; i &lt; SIZE; ++i) {
    if (!board[i]) {
      continue;
    }
    for (int j = i + SIZE; j &lt; i + SIZE_2; j += SIZE) {
      if (board[i] != board[j]) {
        goto next2;
      }
    }
    return board[i];
  next2:;
  }
  for (int i = SIZE + 1; i &lt; SIZE_2; i += SIZE + 1) {
    if (board[i] != *board) {
      goto next3;
    }
  }
  return *board;
next3:
  for (int i = SIZE * 2 - 2; i &lt;= SIZE_2 - SIZE; i += SIZE - 1) {
    if (board[i] != board[SIZE - 1]) {
      return 0;
    }
  }
  return board[SIZE - 1];
}

#define N 100000000

int main(void) {
  srand(0);
  size_t n = 0;
  for (long i = 0; i &lt; N; ++i) {
    int board[SIZE_2];
    for (int i = 0; i &lt; SIZE_2; ++i) {
      board[i] = rand() % 3;
    }
    n += determineResult(board);
  }
  printf("%zu\n", n);
  return EXIT_SUCCESS;
}
I'm going to start with the tough love... both your C and Rust code are unreadable. Nested loops with goto statements is the definition of spaghetti code, and that tends to be the least understandable and unmaintainable type of code. The good news is that you are here, asking for feedback, which is the right direction! ^_^

I'm going to mark suggestions with [universal] if they are just general programming ideas, not specific to Rust.


Rust uses a 4 space indent, not 2.
Ranges don't have spaces. Use 0..20 instead of 0 .. 20.
Don't use the libc crate for random numbers, use the dedicated rand crate. This allows you to not need any unsafe code.
[universal] Always use a random number function for ranges. You should not use the modulus operator % as it can introduce sampling bias, preventing you from having a uniform distribution. In Rust, you can use gen_range.
Use _ when you want to ignore unused variables, like the index value of a loop:

for _ in 0..10 { }

[universal] Program-wide constants like N are terrible names. Give them better names that convey meaning and usage:

const BENCHMARK_ITERATIONS: usize = 50000000;

const SIZE_OF_ROW: usize = 3;
const SIZE_OF_BOARD: usize = SIZE_OF_ROW * SIZE_OF_ROW;

Don't specify the type of a variable when it can be inferred:

let mut board = [0; SIZE_2]; // no `: [i32; SIZE2] on the left

n += determine_result(&amp;board); // no `as usize`

Use iterators instead of manually incrementing an array index. This has improved performance, and prevents you from duplicating the length variable everywhere:

for space in board.iter_mut() {
    *space = 0;

[universal] Declare variables with the minimal scope needed to do the job. For example, your board is only used inside the loop:

for i in 0..10 {
    let mut board = [0; SIZE_2];

Use iterator adapters, like map or fold, instead of doing all the work yourself. These have higher semantic meaning, allowing other programmers to instantly know you are transforming a sequence or reducing it down to a single value. It also allows your code to avoid some mutability.
[universal] Extract out functions, such as one to generate a board. This allows the mutability of the board to be constrained to a very small area.
I'd create a newtype for Board, or at the very least a type alias. This allows you to have higher concepts as opposed to just an array. 
There's no need for explicit return statements at the end of a function.
[universal] Extract out more functions. determine_result has too much going on in it, and breaking them out into functions makes the code way easier to reason about. Functions for "row winners" or "column winners" are identifiable.
[universal] There's no reason to use an i32 for 3 values, that's massive overkill.
The absence of a value isn't really just another value, and you should use Option instead.


All together now:

src/main.rs

extern crate rand;
extern crate itertools;

use rand::{StdRng, Rng, SeedableRng};
use itertools::Itertools;

const SIZE_OF_ROW: usize = 3;
const SIZE_OF_BOARD: usize = SIZE_OF_ROW * SIZE_OF_ROW;

type Board = [Option&lt;Piece&gt;; SIZE_OF_BOARD];

#[derive(Debug, Copy, Clone, PartialEq)]
enum Piece {
    X,
    O,
}

// If every item in an iterator is the same, that value is
// returned. If there are no items or not all items are equal, returns
// None.
fn all_the_same&lt;I&gt;(mut iter: I) -&gt; Option&lt;I::Item&gt;
    where I: Iterator,
          I::Item: PartialEq + ::std::fmt::Debug
{
    let initial_value = match iter.next() {
        Some(v) =&gt; v,
        None =&gt; return None,
    };

    while let Some(value) = iter.next() {
        if initial_value != value {
            return None
        }
    }

    Some(initial_value)
}

fn flatten&lt;T&gt;(opt: Option&lt;Option&lt;T&gt;&gt;) -&gt; Option&lt;T&gt; {
    match opt {
        Some(inner) =&gt; inner,
        None =&gt; None,
    }
}

fn column_winner(board: &amp;Board) -&gt; Option&lt;Piece&gt; {
    (0..3).filter_map(|col| {
        flatten(all_the_same(board.iter().skip(col).step(3)).cloned())
    }).next()
}

fn row_winner(board: &amp;Board) -&gt; Option&lt;Piece&gt; {
    board.chunks(3).filter_map(|row| {
        flatten(all_the_same(row.iter()).cloned())
    }).next()
}

fn top_left_diagonal_winner(board: &amp;Board) -&gt; Option&lt;Piece&gt; {
    flatten(all_the_same(board.iter().step(4)).cloned())
}

fn top_right_diagonal_winner(board: &amp;Board) -&gt; Option&lt;Piece&gt; {
    flatten(all_the_same(board[2..7].iter().step(2)).cloned())
}

fn determine_result(board: &amp;Board) -&gt; Option&lt;Piece&gt; {
    column_winner(board)
        .or_else(|| row_winner(board))
        .or_else(|| top_left_diagonal_winner(board))
        .or_else(|| top_right_diagonal_winner(board))
}

const BENCHMARK_ITERATIONS: usize = 50000000;

fn generate_board&lt;R&gt;(mut rng: R) -&gt; Board
    where R: Rng
{
    let mut board = [None; SIZE_OF_BOARD];
    for space in board.iter_mut() {
        *space = match rng.gen_range(0, 3) {
            1 =&gt; Some(Piece::X),
            2 =&gt; Some(Piece::O),
            _ =&gt; None,
        };
    }
    board
}

fn main() {
    let mut rng = StdRng::from_seed(&amp;[0]);

    let total =
        (0..BENCHMARK_ITERATIONS)
        .map(|_| generate_board(&amp;mut rng))
        .map(|board| determine_result(&amp;board))
        .map(|winner| {
            match winner {
                Some(Piece::X) =&gt; 1,
                Some(Piece::O) =&gt; 2,
                None =&gt; 0,
            }
        })
        .fold(0, |acc, val| acc + val);

    println!("{}", total);
}


Cargo.toml

[package]
name = "tictactoe"
version = "0.1.0"
authors = ["A. Developer &lt;a.developer@example.com&gt;"]

[dependencies]
rand = "*"
itertools = "*"