Super Enums in Rust

Disclaimer: Rust is new to everyone.  You will not understand the language in a single lecture. 

Ferris the crab!

Memory Safety

  • No memory leaks
  • No segmentation faults
  • Ownership system

Speed

  • Compiled and optimized
  • Avoids copying data, referencing it instead
  • No garbage collector

Zero-Cost Abstractions

Complex data structures and language syntax that has no performance cost at run-time.

Strong Developer Tools

  • rustdoc: a documentation tool
  • cargo: package manager; builds and runs projects
  • rustfmt: code formatter

Less Mistakes

  • No more null checks
  • No more forgetting to catch exceptions
  • No more running off arrays or forgetting to deallocate memory
  • No more concurrency issues
  • Errors are caught at compile time

Enumerations

An enum is a set of named values called variants, members, or enumerators.  An instance of an enum can be assigned any one of the enumerators as a value.

A datatype that represents of many possibilities.

Goal

Construct a card data type that can be printed and compared to others.

Let's start with a really bad example.

struct Card {
    int value;
    char suit;

    void print() {
        cout << value << " of " 
             << suit << endl;
    }
};
int main() {
    Card c = Card{2, 'H'};
    Card d = Card{11, 'S'};

    c.print();
    d.print();
}
Card{20, 'B'}

This really sucks.

> ./a.out
2 of H
11 of S
>
struct Card {
    int value;
    char suit;

    Card(int v, char s) {
    	if (v < 1 && v > 13) {
            v = 1;
        }
        if (s != 'S' && s != 'H' &&
            s != 'D' && s != 'C') {
            s = 'S';
        }
       	this.value = v;
        this.suit = s;
    }
};

This still really sucks.

enum Name {
    variant1,
    variant2,
    variant3,
    variant4
}

Enums in C++

enum Suit {
    Spades, Hearts, Diamonds, Clubs
};
enum Value {
    Two, Three, Four, Five, Six, 
    Seven, Eight, Nine, Ten, Jack, 
    Queen, King, Ace
};
struct Card {
    Suit suit;
    Value value;
}
int main() {
    Card c = Card{Two, Hearts};
    Card d = Card{Jack, Spades};
    
    c.print();
    d.print();
}
struct Card {
    Suit suit;
    Value value;

    void print() {
        cout << value << " of " 
             << suit << endl;
    }
}
> ./a.out
0 of 1
9 of 0
>
string valueToString(Value v) {
    if (v == Two) return "Two";
    if (v == Three) return "Three";
    if (v == Four) return "Four";
    if (v == Five) return "Five";
    if (v == Six) return "Six";
    if (v == Seven) return "Seven";
    if (v == Eight) return "Eight";
    if (v == Nine) return "Nine";
    if (v == Ten) return "Ten";
    if (v == Jack) return "Jack";
    if (v == Queen) return "Queen";
    if (v == King) return "King";
    return "Ace";
}
string suitToString(Suit s) {
    if (s == Spades) return "Spades";
    if (s == Hearts) return "Hearts";
    if (s == Diamonds) return "Diamonds";
    return "Clubs";
}
int main() {
    Card c = Card{Two, Hearts};
    Card d = Card{Jack, Spades};
    
    c.print();
    d.print();
}
struct Card {
    Suit suit;
    Value value;

    void print() {
        cout << valueToString(value) 
             << " of " 
             << suitToString(suit)
             << endl;
    }
}
> ./a.out
Two of Hearts
Jack of Spades
>
struct Card {
    // Properties

    bool higher_than(Card c) {
        return value > c.value;
    }
}
int main() {
    Card c = Card{Two, Hearts};
    Card d = Card{Jack, Spades};

    if (c.higher_than(d)) {
        cout << "c is better than d" << endl;
    } else {
        cout << "c is not better than d" << endl;
    }
}
enum Value {
    Two, Three, Four, Five, Six, 
    Seven, Eight, Nine, Ten, Jack, 
    Queen, King, Ace
};
enum Value {
    Two = 2, Three = 3, Four = 4, Five = 5, 
    Six = 6, Seven = 7, Eight = 8, Nine = 9, 
    Ten = 10, Jack = 11, Queen = 12, 
    King = 13, Ace = 1
};
#include <iostream>
#include <string>

using namespace std;

enum Value {
    Two = 2, Three = 3, Four = 4, Five = 5, Six = 6, 
    Seven = 7, Eight = 8, Nine = 9, Ten = 10, Jack = 11, 
    Queen = 12, King = 13, Ace = 1
};

string valueToString(Value v) {
    if (v == Two) return "Two";
    if (v == Three) return "Three";
    if (v == Four) return "Four";
    if (v == Five) return "Five";
    if (v == Six) return "Six";
    if (v == Seven) return "Seven";
    if (v == Eight) return "Eight";
    if (v == Nine) return "Nine";
    if (v == Ten) return "Ten";
    if (v == Jack) return "Jack";
    if (v == Queen) return "Queen";
    if (v == King) return "King";
    return "Ace";
}

enum Suit {
    Spades, Hearts, Diamonds, Clubs
};

string suitToString(Suit s) {
    if (s == Spades) return "Spades";
    if (s == Hearts) return "Hearts";
    if (s == Diamonds) return "Diamonds";
    return "Clubs";
}

struct Card {
    Value value;
    Suit suit;

    void print() {
        cout << valueToString(value) << " of " << suitToString(suit) << endl;
    }

    bool higher_than(Card c) {
        return value > c.value;
    }
};

int main() {
    Card c = Card{Two, Hearts};
    Card d = Card{Jack, Spades};

    c.print();
    d.print();

    if (c.higher_than(d)) {
        cout << "c is better than d" << endl;
    } else {
        cout << "c is not better than d" << endl;
    }
}

C++ Enums

Pros

Cons

  • More restrictive than integers and strings
  • Can't insert a wrong value
  • Every variant is a number
  • Every variant is a number
  • Every variant is brought into scope
  • Converting to strings is a nightmare
  • Can't have methods
enum Name {
    variant1,
    variant2,
    variant3,
    variant4
}

Enums in Java

enum Value {
    Two, Three, Four, Five, Six,
    Seven, Eight, Nine, Ten, Jack,
    Queen, King, Ace
}
enum Suit {
    Spades, Hearts, Diamonds, Clubs
}
static class Card {
    Value value;
    Suit suit;

    public Card(Value v, Suit s) {
        value = v;
        suit = s;
    }
}
public static void main(String[] args) {
    Card c = new Card(Value.Two, Suit.Hearts);
    Card d = new Card(Value.Jack, Suit.Spades);
}
static class Card {
    // Properties
    // Constructor
    
    public void print() {
        System.out.println(this.value.toString() 
                + " of " + this.suit.toString());
    }
}
public static void main(String[] args) {
    Card c = new Card(Value.Two, Suit.Hearts);
    Card d = new Card(Value.Jack, Suit.Spades);

    c.print();
    d.print();
}
> java main
Two of Hearts
Jack of Spades
>
static public int valueOf(Value v) {
    if (v == Value.Two) return 2;
    if (v == Value.Three) return 3;
    if (v == Value.Four) return 4;
    if (v == Value.Five) return 5;
    if (v == Value.Six) return 6;
    if (v == Value.Seven) return 7;
    if (v == Value.Eight) return 8;
    if (v == Value.Nine) return 9;
    if (v == Value.Ten) return 10;
    if (v == Value.Jack) return 11;
    if (v == Value.Queen) return 12;
    if (v == Value.King) return 13;
    return 1;
 }
static class Card {
    // Properties
    // Methods
    
    public boolean higherThan(Card c) {
        return valueOf(this.value) > valueOf(c.value);
    }
}
public static void main(String[] args) {
    Card c = new Card(Value.Two, Suit.Hearts);
    Card d = new Card(Value.Jack, Suit.Spades);

    if (c.higherThan(d)) {
        System.out.println("c is better than d");
    } else {
        System.out.println("c is not better than d");
    }
}
class main {

    enum Value {
        Two, Three, Four, Five, Six,
        Seven, Eight, Nine, Ten, Jack,
        Queen, King, Ace
    }

    static public int valueOf(Value v) {
        if (v == Value.Two) return 2;
        if (v == Value.Three) return 3;
        if (v == Value.Four) return 4;
        if (v == Value.Five) return 5;
        if (v == Value.Six) return 6;
        if (v == Value.Seven) return 7;
        if (v == Value.Eight) return 8;
        if (v == Value.Nine) return 9;
        if (v == Value.Ten) return 10;
        if (v == Value.Jack) return 11;
        if (v == Value.Queen) return 12;
        if (v == Value.King) return 13;
        return 1;
    }

    enum Suit {
        Spades, Hearts, Diamonds, Clubs
    }

    static class Card {
        Value value;
        Suit suit;

        public Card(Value v, Suit s) {
            value = v;
            suit = s;
        }

        public void print() {
            System.out.println(this.value.toString() 
            	+ " of " + this.suit.toString());
        }

        public boolean higherThan(Card c) {
            return valueOf(this.value) > valueOf(c.value);
        }
    }

    public static void main(String[] args) {
        Card c = new Card(Value.Two, Suit.Hearts);
        Card d = new Card(Value.Jack, Suit.Spades);

        c.print();
        d.print();

        if (c.higherThan(d)) {
            System.out.println("c is better than d");
        } else {
            System.out.println("c is not better than d");
        }
    }
}

Java Enums

Pros

Cons

  • Easily converted to strings
  • Aren't numbers
  • Variants are scoped by the name of the enum
  • Variants are scoped by the name of the enum
  • Can't have methods easily
  • Can't guarantee check on all variants
enum Name {
    variant1,
    variant2,
    variant3,
    variant4
}

Enums in Rust

Rust Syntax in 4.5 Minutes

Hello World!

fn main() {
    println!("Hello, world!");
}

Variables and Mutability

 

fn main() {
    let hourly_rate = 10;
    let mut money = 0;
    
    let name = "Bob";
    money += hourly_rate * 2;
    
    println!("{} made {} dollars today.", 
    	name,
        money
    )
}
> ./example
Bob made 20 dollars today.
>

Conditionals

fn main() {
    println!("Would you like some cookies?");
    
    let num_cookies = 10;
    
    if num_cookies > 0 {
        println!("They look delicious!");
    } else {
        println!("Where are they?");
    }
}

Type Annotation and Casting

fn main() {
    let x: i32 = 50;
    let y: i32 = 20;
    let z: i32 = x - y;
    
    let m_pi: f32 = 3.14159;
    let e_pi = m_pi as i32;
}

Datatypes

i8
i16
i32
i64
i128
isize
 
u8
u16
u32
u64
u128
usize
 

Integers

Booleans

Characters

Floats

bool
char
f32, f64

Strings and String Slices

fn main() {
    let me: String = String::from("Dave");
    let him: &str = "Ryan";
    
    println!("{} has methods. {} does not.",
    	me,
        him,
    )
    
    let enhance: String = him.to_string();
    println!("Now {} has methods.");
}

Loops

fn main() {
    let mut i = 0;
    while i != 5 {
        println!("{}", i);
        i++;
    }
}
fn main() {
    for i in 0..5 {
        println!("{}");
    }
}

Vectors

fn main() {
    let mut profs: Vec<&str> = vec![
        "Gupta", "Chen", "Linos",
        "Sorenson", "Rybarczyk"
    ];
    profs.push("Partenheimer");
    
    for prof in profs {
        println!("{} is the best!", prof);
    }
    
    println!("But {} is my favorite.", profs[0]);
}

Functions

fn can_ride(age: u32, height: f32) -> bool {
    if age >= 10 || height > 3.5 {
    	return true;
    } else {
    	return false;
    }
}
fn main() {
    let allowed = can_ride(8, 4.1);
}

Return Values

fn main() {
    let her_age = 17;
    
    // my_status is dating or single
    let my_status = if her_age < 18 {
        "single"
    } else {
        "dating"
    };
}

Return Values

fn can_ride(age: u32, height: f32) -> bool {
    if age >= 10 || height > 3.5 {
    	return true;
    } else {
    	return false;
    }
}
fn can_ride(age: u32, height: f32) -> bool {
    if age >= 10 || height > 3.5 {
    	true
    } else {
    	false
    }
}
fn can_ride(age: u32, height: f32) -> bool {
    age >= 10 || height > 3.5
}

Structs

struct Fish {
    num_eyes: i32,
    color: String,
}
fn main() {
    let gregory = Fish {
        num_eyes = 5,
        color = "red".to_string(),
    };
}
impl Fish {
    // static
    fn new(e: i32, c: String) -> Fish {
        Fish {
            num_eyes: e,
            color: c,
        }
    }
	
    // read only
    fn print(&self) {
        println!("I'm {} with {} eyes.",
            self.color,
            self.num_eyes,
        )
    }
    
    // read and mutate
    fn remove_eyes(&mut self) {
        self.num_eyes = 0;
    }
}
fn main() {
    let mut gregory = Fish::new(
        5, "red".to_string()
    );
    gregory.print();
    gregory.remove_eyes();
    gregory.print();
}
> ./example
I'm red with 5 eyes.
I'm red with 0 eyes.
>

That's time!

enum Name {
    variant1,
    variant2,
    variant3,
    variant4
}

Enums in Rust

enum Value {
    Two, Three, Four, Five, Six,
    Seven, Eight, Nine, Ten, Jack,
    Queen, King, Ace
}
enum Suit {
    Spades, Hearts, Diamonds, Clubs
}
struct Card {
    suit: Suit,
    value: Value,
}
fn main() {
    let c = Card { value: Value::Two, suit: Suit::Hearts };
    let d = Card { value: Value::Jack, suit: Suit::Spades };
}
fn main() {
    use Value::*;
    use Suit::*;
    
    let c = Card { value: Two, suit: Hearts };
    let d = Card { value: Jack, suit: Spades };
}
struct Card {
    suit: Suit,
    value: Value,
}

impl Card {
    fn print(&self) {
        println!("{:?} of {:?}", self.value, self.suit);
    }
}
fn main() {
    use Value::*;
    use Suit::*;
    let c = Card { value: Two, suit: Hearts };
    let d = Card { value: Jack, suit: Spades };
    c.print();
    d.print();
}
impl Value {
    fn to_int(&self) -> i32 {
        use Value::*;
        match self {
            Two => 2,
            Three => 3,
            Four => 4,
            Five => 5,
            Six => 6,
            Seven => 7,
            Eight => 8,
            Nine => 9,
            Ten => 10,
            Jack => 11,
            Queen => 12,
            King => 13,
            Ace => 1,
        }
    }
}
impl Card {
    fn higher_than(&self, c: Card) -> bool {
        self.value.to_int() > c.value.to_int()
    }
}
fn main() {
    use Value::*;
    use Suit::*;
    let c = Card { value: Two, suit: Hearts };
    let d = Card { value: Jack, suit: Spades };
    c.print();
    d.print();

    if c.higher_than(d) { 
        println!("c is better than d");
    } else {
        println!("c is not better than d");
    }
}
#[derive(Debug)]
enum Value {
    Two, Three, Four, Five, Six,
    Seven, Eight, Nine, Ten, Jack,
    Queen, King, Ace
}

impl Value {
    fn to_int(&self) -> i32 {
        use Value::*;
        match self {
            Two => 2,
            Three => 3,
            Four => 4,
            Five => 5,
            Six => 6,
            Seven => 7,
            Eight => 8,
            Nine => 9,
            Ten => 10,
            Jack => 11,
            Queen => 12,
            King => 13,
            Ace => 1,
        }
    }
}

#[derive(Debug)]
enum Suit {
    Spades, Hearts, Diamonds, Clubs
}

struct Card {
    suit: Suit,
    value: Value,
}

impl Card {
    fn print(&self) {
        println!("{:?} of {:?}", self.value, self.suit);
    }

    fn higher_than(&self, c: Card) -> bool {
        self.value.to_int() > c.value.to_int()
    }
}

fn main() {
    use Value::*;
    use Suit::*;

    let c = Card { value: Two, suit: Hearts };
    let d = Card { value: Jack, suit: Spades };

    c.print();
    d.print();

    if c.higher_than(d) { 
        println!("c is better than d");
    } else {
        println!("c is not better than d");
    }
}

Rust Enums

Pros

Cons

  • Opt in to bring variants into scope
  • Can have methods
  • Aren't numbers
  • Easy conversion to strings
  • Match statement ensures comparisons can't go wrong
  • As powerful at structs

Structs vs. Enums

enum Name {
    variant1,
    variant2,
    variant3,
    variant4
}
struct Name {
    key1: type1;
    key2: type2;
    key3: type3;
    key4: type4;
}

simultaneous properties

exactly one property

stores data for each property

no data stored

this really sucks

Integers

Rationals

Reals

Complex

379
0
3
-89
\frac{-1}{12}
\frac{59}{4}
23.5
42.983...
\pi
e
1+\sqrt 5
-34.2 - i
59i
89-29i
e+2i

Number

37
enum Number {
    Integer(i32),
    Rational(i32, i32),
    Real(f32),
    Complex(f32, f32),
}
enum Number {
    Integer,
    Rational,
    Real,
    Complex,
}
enum Name {
    variant1(type1),
    variant2(type2),
    variant3(type3),
    variant4(type4)
}

Enums in Rust

enum Number {
    Integer(i32),
    Rational(i32, i32),
    Real(f32),
    Complex(f32, f32),
}
fn main() {
    let a = Number::Integer(-4);
    let b = Number::Rational(7, 8);
    let c = Number::Real(3.141592653);
    let d = Number::Complex(-1.0, 2.0);
}
impl Number {
    fn print(&self) {
        use Number::*;
        match self {
            Integer(n) => println!("{}", n),
            Rational(n, d) => println!("{}/{}", n, d),
            Real(x) => println!("{}", x),
            Complex(a, b) => println!("{} + {}i", a, b),
        }
    }
}
impl Number {
    fn negate(self) -> Number {
        use Number::*;
        match self {
            Integer(n) => Integer(-n),
            Rational(n, d) => Rational(-n, d),
            Real(x) => Real(-x),
            Complex(a, b) => Complex(-a, -b),
        }
    }
}
enum Pet {
    Dog(String),
    Cat(String),
    Turtle,
    Squirrel
}
fn main() {
    let cali = Pet::Cat("Calico".to_string());
    let fred = Pet::Turtle;
}
impl Pet {
    fn get_breed(&self) -> String {
        use Pet::*;
        match self {
            Dog(breed) => breed.to_string(),
            Cat(breed) => breed.to_string(),
            _ => "this pet has no breed".to_string(),
        }
    }
}
> ./example
My pet's breed is Calico
My pet's breed is this pet has no breed
>
fn main() {
    let cali = Pet::Cat("Calico".to_string());
    let fred = Pet::Turtle;
    let pets = vec![cali, fred];
    
    for pet in pets {
        println!("My pet's breed is {}", pet.get_breed());
    }
}
impl Pet {
    fn has_breed(&self) -> bool {
        use Pet::*;
        match self {
            Turtle => false,
            Squirrel => false,
            _ => true,
        }
    }
}
fn main() {
    let cali = Pet::Cat("Calico".to_string());
    let fred = Pet::Turtle;
    let pets = vec![cali, fred];
    
    for pet in pets {
        if pet.has_breed() {
            println!("This pet is a {}", pet.get_breed());
        } else {
            println!("This pet no breed.")
        }
    }
}
impl Pet {
    fn get_breed(&self) -> Breed {
        use Pet::*;
        match self {
            Dog(breed) => Breed::Has(breed),
            Cat(breed) => Breed::Has(breed),
            _ => Breed::NoBreed,
        }
    }
}
enum Breed {
    Has(String),
    NoBreed
}

Option Type

pub enum Option<T> {
    None,
    Some(T),
}

Replaces null values!

impl Pet {
    fn get_breed(&self) -> Option<String> {
        use Pet::*;
        match self {
            Dog(breed) => Some(breed.to_string()),
            Cat(breed) => Some(breed.to_string()),
            _ => None,
        }
    }
}
fn main() {
    let cali = Pet::Cat("Calico".to_string());
    let fred = Pet::Turtle;
    let pets = vec![cali, fred];
    
    for pet in pets {
        // Can't work! Can't print Option!
        println!("My pet's breed is {}", pet.get_breed());
    }
}
fn main() {
    let cali = Pet::Cat("Calico".to_string());
    let fred = Pet::Turtle;
    let pets = vec![cali, fred];
    
    for pet in pets {
        match pet.get_breed() {
            Some(breed) => println!("My pet is a {}", breed),
            None => println!("My pet has no breed"),
        }
    }
}
enum Pet {
    Dog(String),
    Cat(String),
    Turtle,
    Squirrel
}

impl Pet {
    fn get_breed(&self) -> Option<String> {
        use Pet::*;
        match self {
            Dog(breed) => Some(breed.to_string()),
            Cat(breed) => Some(breed.to_string()),
            _ => None,
        }
    }
}

fn main() {
    let cali = Pet::Cat("Calico".to_string());
    let fred = Pet::Turtle;

    let pets = vec![cali, fred];
    for pet in pets {
        match pet.get_breed() {
            Some(breed) => println!("My pet is a {}", breed),
            None => println!("My pet has no breed"),
        }
    }
}

Integers

Rationals

Reals

Complex

379
0
3
-89
\frac{-1}{12}
\frac{59}{4}
23.5
42.983...
\pi
e
1+\sqrt 5
-34.2 - i
59i
89-29i
e+2i

Number

37
impl Number {
    fn into_complex(&self) -> Number {
        use Number::*;
        match *self {
            Integer(n) => Complex(n as f32, 0.0),
            Rational(n, d) => {
                let f = n as f32 / d as f32;
                Complex(f, 0.0)
            },
            Real(x) => Complex(x, 0.0),
            Complex(a, b) => Complex(a, b),   
        }
    }
}
impl Number {
    fn into_real(&self) -> Number {
        use Number::*;
        match *self {
            Integer(n) => Real(n as f32),
            Rational(n, d) => {
                let f = n as f32 / d as f32;
                Real(f)
            },
            Real(x) => Real(x),
            Complex(a, b) => Real(a), // gross
        }
    }
}

Error Handling in Java

class main {
    public static void main(String[] args) {
        int a = 10, b = 0;
        int quotient;
        
        try {
            // would crash program
            quotient = a / b;
        } catch (Exception ex) {
            // but its caught and dealt with here
            System.out.println("Divided by 0!");
            System.out.println(ex.getMessage());
        }
        
    	System.out.println("a / b = " + quotient);
    }
}
class main {
    public static void main(String[] args) {
        int a = 10, b = 0;
        int quotient;
        
        try {
            // would crash program
            quotient = Integer.division(a, b);
        } catch (Exception ex) {
            // but its caught and dealt with here
            System.out.println("Divided by 0!");
            System.out.println(ex.getMessage());
        }
    
    	System.out.println("a / b = " + quotient);
    }
}
class main {
    public static void main(String[] args) {
        int a = 10, b = 0;
        int product;
        
        try {
            // can NEVER crash program
            product = Integer.product(a, b);
        } catch (Exception ex) {
            // none of this will ever be run
            System.out.println("Error!");
            System.out.println(ex.getMessage());
        }
    
    	System.out.println("a * b = " + product);
    }
}

Result Type

pub enum Result<T, E> {
    Ok(T),
    Err(E),
}

Replaces exception handling!

No more try-catching!

impl Number {
    fn into_real(&self) -> Result<Number, String> {
        use Number::*;
        match *self {
            Integer(n) => Ok(Real(n as f32)),
            Rational(n, d) => {
                let f = n as f32 / d as f32;
                Ok(Real(f))
            }, 
            Real(x) => Ok(Real(x)),
            Complex(a, b) => {
                if b == 0.0 {
                    Ok(Real(a))
                } else {
                    Err("Imaginary part is nonzero".to_string())
                }
            },
        }
    }
}
fn main() {
    let a = Number::Integer(-4);
    let b = Number::Rational(7, 8);
    let c = Number::Real(3.141592653);
    let d = Number::Complex(-1.0, 2.0);
    let nums = vec![a, b, c, d];
    
    for num in &nums {
        // No try catch needed!  This will NEVER fail!
        let z = num.into_complex();
        z.print();
    }
}
fn main() {
    let a = Number::Integer(-4);
    let b = Number::Rational(7, 8);
    let c = Number::Real(3.141592653);
    let d = Number::Complex(-1.0, 2.0);
    let nums = vec![a, b, c, d];
    
    for num in &nums {
        // Doesn't work! Can't ignore failure!
        // num.into_real().print();
        match num.into_real() {
            Ok(x) => x.print(),
            Err(s) => println!("{}", s),
        }
    }
}
#[derive(Clone, Copy)]
enum Number {
    Integer(i32),
    Rational(i32, i32),
    Real(f32),
    Complex(f32, f32),
}

impl Number {

    fn print(&self) {
        use Number::*;
        match self {
            Integer(n) => println!("{}", n),
            Rational(n, d) => println!("{}/{}", n, d),
            Real(x) => println!("{}", x),
            Complex(a, b) => println!("{} + {}i", a, b),
        }
    }

    fn negate(self) -> Number {
        use Number::*;
        match self {
            Integer(n) => Integer(-n),
            Rational(n, d) => Rational(-n, d),
            Real(x) => Real(-x),
            Complex(a, b) => Complex(-a, -b),
        }
    }

    fn into_complex(&self) -> Number {
        use Number::*;
        match *self {
            Integer(n) => Complex(n as f32, 0.0),
            Rational(n, d) => {
                let f = n as f32 / d as f32;
                Complex(f, 0.0)
            },
            Real(x) => Complex(x, 0.0),
            Complex(a, b) => Complex(a, b),   
        }
    }

    // Talk about why we can't just return REAL
    fn into_real(&self) -> Result<Number, String> {
        use Number::*;
        match *self {
            Integer(n) => Ok(Real(n as f32)),
            Rational(n, d) => {
                let f = n as f32 / d as f32;
                Ok(Real(f))
            }, 
            Real(x) => Ok(Real(x)),
            Complex(a, b) => {
                if b == 0.0 {
                    Ok(Real(a))
                } else {
                    Err("Imaginary part is nonzero".to_string())
                }
            },
        }
    }
}

fn main() {
    let a = Number::Integer(-4);
    let b = Number::Rational(7, 8);
    let c = Number::Real(3.141592653);
    let d = Number::Complex(-1.0, 2.0);

    let nums = vec![a, b, c, d];
    for num in &nums {
        num.print();
    }
    println!();
    for num in &nums {
        // No try catch needed!  This will NEVER fail!
        num.into_complex().print();
    }
    println!();
    for num in &nums {
        // Doesn't work!
        // num.into_real().print();
        match num.into_real() {
            Ok(x) => x.print(),
            Err(s) => println!("{}", s),
        }
    }
}

Let's Write Wumpus!

pub enum Player {
    Alive { arrows: usize, location: usize },
    Dead(String),
    Win,
}
enum Hazard {
    Wumpus,
    Bats,
    Pitfall,
}
struct Room {
    adjacent: [usize; 3],
    hazard: Option<Hazard>,
}
pub struct Cave {
    rooms: Vec<Room>,
}
pub enum Action {
    Move(usize),
    Shoot(usize),
    Quit,
    Help,
}
use std::io;
fn main() {
    let mut buffer = String::new();
    io::stdin().read_line(&mut buffer).unwrap();
    let x = buffer.trim().parse::<i32>().unwrap();
}
#include <iosteam>
using namespace std;
int main() {
    int x;
    cin >> x;
}
import java.util.Scanner;
class main {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        int x = input.nextInt();
    }
}

super_enums

By rutrum

super_enums

A talk given through Butler University ACM on November 13th 2019.

  • 378