14

I'd like to write a function which toggles/switches the provided value to the next in the enum and wraps around at the end:

enum Direction { NORTH, SOUTH, EAST, WEST }

For example, NORTH => SOUTH, SOUTH => EAST, EAST => WEST, WEST => NORTH.

Is there an easier way than manually creating a static array as described in In Rust, is there a way to iterate through the values of an enum?

use Direction::*;
static DIRECTIONS: [Direction; 4] = [NORTH, SOUTH, EAST, WEST];

Aren't enums suppose to be "enumerated"? I vaguely remember seeing an example before in Rust, but I can't seem to find it. Since Rust enums are more like unions/variants, I guess this complicates things.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Jacob Wang
  • 4,411
  • 5
  • 29
  • 43
  • possible duplicate of [In Rust, is there a way to iterate through the values of an enum?](http://stackoverflow.com/questions/21371534/in-rust-is-there-a-way-to-iterate-through-the-values-of-an-enum) – Chris Morgan Sep 16 '14 at 14:09
  • 1
    it should be possible to do it unsafely: `let d : Direction = unsafe {std::mem::transmute((NORTH as u8) + 1) }; // SOUTH`. Use at your own risk... – Paolo Falabella Sep 16 '14 at 14:20

3 Answers3

16

I would prefer to explicitly encode the next direction via a match statement:

#[derive(Debug)]
enum Direction {
    North,
    South,
    East,
    West,
}

impl Direction {
    fn turn(&self) -> Self {
        use Direction::*;
        match *self {
            North => South,
            South => East,
            East => West,
            West => North,
        }
    }
}

fn main() {
    use Direction::*;

    for i in &[North, South, East, West] {
        println!("{:?} -> {:?}", i, i.turn());
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
13

Vladimir's answer is correct but the programmer must remember to change the magic number "4" when adding a new member to the enum. This definition for turn should be easier to maintain:

#[derive(Debug, Copy, Clone, FromPrimitive)]
enum Direction {
    NORTH = 0,
    SOUTH,
    EAST,
    WEST,
}

fn turn(d: Direction) -> Direction {
    match FromPrimitive::from_u8(d as u8 + 1) {
        Some(d2) => d2,
        None => FromPrimitive::from_u8(0).unwrap(),
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
gfour
  • 959
  • 6
  • 9
  • I like your solution! Your solution is arguably more resistent to change (bit more verbose, but still easy to reason about). Thanks! – Jacob Wang Sep 17 '14 at 09:17
7

I guess something like this will do the trick:

#[macro_use]
extern crate num_derive;
extern crate num_traits;

use num_traits::FromPrimitive;

#[derive(Debug, Copy, Clone, FromPrimitive)]
enum Direction {
    NORTH = 0,
    SOUTH,
    EAST,
    WEST,
}

fn turn(d: Direction) -> Direction {
    FromPrimitive::from_u8((d as u8 + 1) % 4).unwrap()
}

fn main() {
    use Direction::*;
    for &d in [NORTH, SOUTH, EAST, WEST].iter() {
        println!("{:?} -> {:?}", d, turn(d));
    }
}

This does not require unsafe as it uses the automatically derived FromPrimitive trait.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Vladimir Matveev
  • 120,085
  • 34
  • 287
  • 296
  • I was hoping there would be a safe way (which is why I wrote a comment instead of proposing the unsafe transmute as an answer), but your method still relies on creating an array with all the enum's variants, which OP mentions as a possibility he's already aware of – Paolo Falabella Sep 16 '14 at 15:47
  • @PaoloFalabella, what do you mean, it requires creating an array? It doesn't; the array is there just to illustrate the point. The main work is done in `turn()` function which is safe and does not use arrays. – Vladimir Matveev Sep 16 '14 at 17:02