Skip to content
📖 Auto vs RustBook contents

Enums and Pattern Matching ​

In this chapter, we'll look at enumerations, also referred to as enums. Enums allow you to define a type by enumerating its possible variants. First we'll define and use an enum to show how an enum can encode meaning along with data. Next, we'll explore a particularly useful enum, called Option, which expresses that a value can be either something or nothing. Then, we'll look at how pattern matching with the is keyword makes it easy to run different code for different values of an enum. Finally, we'll cover how Auto's ?? operator provides a concise way to handle optional values.

Defining an Enum ​

Where structs give you a way of grouping together related fields and data, like a Rectangle with its width and height, enums give you a way of saying a value is one of a possible set of values. For example, we may want to say that Rectangle is one of a set of possible shapes that also includes Circle and Triangle. To do this, Auto allows us to encode these possibilities as an enum.

Let's look at a situation we might want to express in code and see why enums are useful and more appropriate than structs in this case. Say we need to work with directions. We can enumerate all possible variants, which is where enumeration gets its name.

Defining a `Direction` enum

Direction is now a custom data type that we can use elsewhere in our code. Note the key differences from Rust:

FeatureAutoRust
Access variantDirection.NorthDirection::North
Display output${dir} (auto Display)Requires #[derive(Display)]

Auto uses dot notation (.) to access enum variants, while Rust uses the double colon (::). Auto also automatically generates a Display implementation for enums, allowing you to use them directly in string interpolation.

Enum Values ​

We can create instances of each variant and pass them to functions:

Passing enum values to functions

Enums with Data ​

One advantage of enums over structs is that each variant can hold different types and amounts of data. Rather than an enum inside a struct, we can put data directly into each enum variant:

An `IpAddr` enum whose variants store `String` values

Each variant can have different types and amounts of associated data. Here's an example with a mix of empty variants and variants with data:

A `Message` enum with different data in variants

Key differences:

FeatureAutoRust
Empty variantQuit (no parentheses)Quit
Data variantWrite StringWrite(String)
Construct with dataMessage.Write("hello")Message::Write(String::from("hello"))

Methods on Enums ​

Just like with structs, we can use the ext keyword to define methods on enums:

Defining methods on an enum with `ext`

The ext keyword works the same way for enums as it does for structs — it adds methods (generating Rust impl blocks) with implicit self and .field shorthand.

The Option Enum ​

Auto's Option type is expressed using the ?T syntax, which represents a value that could be something or nothing. This is Auto's equivalent of Rust's Option<T>:

auto
// ?T is syntactic sugar for May<T> (Auto's Option type)
// It can be Some(value) or None
rust
// Option<T> can be Some(value) or None

The ?T syntax eliminates the null reference problem that Tony Hoare famously called his "billion-dollar mistake." Instead of null, Auto makes you explicitly handle the case where a value might be absent.

Using `?T` as a return type with `Some` and `None`

Key differences for Option:

FeatureAutoRust
Option type?TOption&lt;T&gt;
Has valueSome(value)Some(value)
No valueNoneNone
Function returnfn foo() ?intfn foo() -&gt; Option&lt;i32&gt;

Because ?T and T are different types, the compiler won't let you use an optional value as if it were definitely present. You must explicitly handle the None case.

Pattern Matching with is ​

Auto uses the is keyword for pattern matching, which is equivalent to Rust's match expression. The is expression compares a value against a series of patterns and executes code based on which pattern matches.

Basic Pattern Matching ​

Pattern matching on an enum with `is`

Key differences:

FeatureAutoRust
Match keywordis value { }match value { }
Wildcard armelse =&gt;_ =&gt;
Arm separatornewline (no commas)commas between arms
Pattern syntaxVariant.Name(binding)Variant::Name(binding)

Patterns That Bind to Values ​

One of the most powerful features of is is that patterns can bind to the values inside enum variants:

Destructuring enum variants with `is`

When atom is Atom.Int(11), the pattern Atom.Int(i) matches and i binds to the value 11. The bound variable can then be used in the arm's code.

Matching Option with is ​

We can use is to handle ?T (Option) values, just as we would with any other enum:

Using `is` to match on `?int` (Option) values

Matches Are Exhaustive ​

Just like Rust's match, Auto's is is exhaustive — the compiler will ensure that you handle every possible case. If you forget a case, you'll get a compile-time error.

Catch-All with else ​

The else arm is the catch-all pattern, equivalent to Rust's _:

Catch-all pattern with `else`

The else arm matches any value not specifically listed in other arms. Note that else must come last, since arms are evaluated in order.

Concise Optional Handling with ?? ​

The ?? (null coalescing) operator provides a concise way to unwrap an optional value with a default:

The `??` null coalescing operator

The ?? operator is Auto's equivalent of Rust's .unwrap_or() method. If the left side has a value, it returns that value; otherwise, it returns the right side as a default.

This is useful when you want to provide a fallback value without the verbosity of a full is match:

auto
// Verbose: full is match
let result = is optional_value {
    Some(v) => v
    None => default_value
}

// Concise: ?? operator
let result = optional_value ?? default_value

Summary ​

Auto's enums and pattern matching provide the same power as Rust's, with a more streamlined syntax:

ConceptAutoRust
Define enumenum Name { ... }enum Name { ... }
Access variantName.VariantName::Variant
Data variantVariant TypeVariant(Type)
Pattern matchis value { }match value { }
Wildcard armelse =&gt;_ =&gt;
Option type?TOption&lt;T&gt;
Some valueSome(value)Some(value)
No valueNoneNone
Null coalescex ?? defaultx.unwrap_or(default)
Enum methodsext Name { }impl Name { }

Enums are a powerful tool in your Auto toolbox. Combined with structs from Chapter 5, you can create custom types that express the concepts in your program's domain. The is pattern matching and ?? operator make working with enums and optional values both safe and concise.

Let's move on to Chapter 7 and look at how Auto organizes code with packages and modules.

Released under the MIT License.