1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
//! No. more. nulls.
//!
//!   - **Date:** October 21, 2015
//!   - **Subject:** Enumerated (`enum`) types, pattern matching, and meaningful
//!     return values.
//!   - [**Audio**][mp3]
//!
//! [mp3]: https://www.podtrac.com/pts/redirect.mp3/cdn.newrustacean.com/file/newrustacean/e003.mp3
//!
//! <audio style="width: 100%" title="No. more. nulls." controls preload=metadata src="https://www.podtrac.com/pts/redirect.mp3/cdn.newrustacean.com/file/newrustacean/e003.mp3" />
//!
//! # Notes
//!
//! Today's episode discusses, in order:
//!
//!   - Enumerated types, with an eye to the difference between structs and
//!     enums, and to the differences between `enum`s in C and in Rust.
//!   - Pattern matching, with a focus on using them with enumerated types and
//!     some discussion about how they differ from `switch` blocks in C-like
//!     languages.
//!   - Using the `Option` and `Result` enumerated types with pattern matching
//!     to provide meaningful returns from functions safely.
//!
//! ## Order
//! There is a specific order to the examples below, and it is *not* the
//! automatically-alphabetized order rendered by `rustdoc`. Instead, you should
//! work through in the sequence they appear in the [source]:
//!
//!  1. [`RelatedishThings`]
//!  2. [`demonstrate_basic_enumeration`]
//!  3. [`demonstrate_match`]
//!  4. [`get_an_option`]
//!  5. [`demonstrate_option`]
//!  6. [`get_a_result`]
//!  7. [`demonstrate_result`]
//!
//! [source]: /src/show_notes/e003.rs.html
//! [`RelatedishThings`]: /show_notes/e003/enum.RelatedishThings.html
//! [`demonstrate_basic_enumeration`]: /show_notes/e003/fn.demonstrate_basic_enumeration.html
//! [`demonstrate_match`]: /show_notes/e003/fn.demonstrate_match.html
//! [`get_an_option`]: /show_notes/e003/fn.get_an_option.html
//! [`demonstrate_option`]: /show_notes/e003/fn.demonstrate_option.html
//! [`get_a_result`]: /show_notes/e003/fn.get_a_result.html
//! [`demonstrate_result`]: /show_notes/e003/fn.demonstrate_result.html
//!
//! ## Links
//!
//!   - New Rustacean [Pull Request #1][link-1]
//!   - Work on IDE support!
//!       + [Landing page][link-2]
//!       + My chosen tool: [JetBrains/IntelliJ][link-3]
//!   - [Rustlings][link-4]
//!   - [Rust FFI Omnibus][link-5]
//!
//! [link-1]: https://github.com/chriskrycho/newrustacean.com/pull/1
//! [link-2]: https://www.rust-lang.org/ides.html
//! [link-3]: https://github.com/alexeykudinkin/intellij-rust
//! [link-4]: https://github.com/carols10cents/rustlings
//! [link-5]: http://jakegoulding.com/rust-ffi-omnibus/basics/
//!
//! # Follow/Support
//!
//!   - New Rustacean:
//!     + Twitter: [@newrustacean](https://www.twitter.com/newrustacean)
//!       + App.net: [@newrustacean](https://alpha.app.net/newrustacean)
//!       + <a href="https://www.patreon.com/newrustacean" rel="payment">Patreon</a>
//!     + Email: [hello@newrustacean.com](mailto:hello@newrustacean.com)
//!   - Chris Krycho
//!     + Twitter: [@chriskrycho](https://www.twitter.com/chriskrycho)
//!       + App.net: [@chriskrycho](https://alpha.app.net/chriskrycho)

/// Just exists to be used as an element in `RelatedishThings`.
#[derive(Debug)]
pub struct PreexistingStruct {
    pub some_int: i32,
    pub some_string: String,
}

/// An enumeration can *hold* a variety of types. This one shows you a few.
///
/// Note: using an `enum` this way is actually crazy. The types should usually
/// have some relationship to each other. Here, they don't. The *only* reason I
/// have them together like this is to show you that these aren't just integers.
/// Enums in Rust can have members of any other type.
///
/// As [Rust by Example][1] [puts it][2]:
///
/// > Any variant which is valid as a `struct` is also valid as an `enum`.
///
/// What's the difference between an `enum` and `struct`? An `enum` is only ever
/// *one* of the options which comprise it, whereas a `struct` is always *all*
/// the elements which comprise it.
///
/// One enormous benefit of `enum` types is that, when they are the return value
/// of a function (as in the examples below), they *must* be handled.
///
/// [1]:http://rustbyexample.com/
/// [2]: http://rustbyexample.com/custom_types/enum.html
#[derive(Debug)]
pub enum RelatedishThings {
    /// This doesn't have a value other than being RelatedishThings::Unit.
    Unit,
    /// It could be a tuple struct, with basically any value type embedded.
    SomeName(String),
    SomeValue(i32),
    /// It can be a full-on struct-type construct.
    ComplexData {
        description: String,
        number: String,
    },
    /// And it can use other complex data types within those, of course.
    ReusedStructure(PreexistingStruct),
}

/// Shows how returning a `RelatedishThings::Unit` instance works.
fn get_unit() -> RelatedishThings {
    RelatedishThings::Unit
}

/// Shows how returning a `RelatedishThings::SomeName` instance works.
fn get_name() -> RelatedishThings {
    RelatedishThings::SomeName("New Rustacean".to_string())
}

/// Shows how returning a `RelatedishThings::SomeValue` instance works.
fn get_value() -> RelatedishThings {
    RelatedishThings::SomeValue(42)
}

/// Shows how returning a `RelatedishThings::ComplexData` instance works.
fn get_complex_data() -> RelatedishThings {
    RelatedishThings::ComplexData {
        description: "A fun podcast!".to_string(),
        number: "e003".to_string(),
    }
}

/// Shows how returning a `RelatedishThings::ReusedStructure` instance works.
fn get_reused_structure() -> RelatedishThings {
    // First create a struct from our (silly) PreexistingStruct type.
    let s = PreexistingStruct {
        some_int: 42,
        some_string: "Hey".to_string(),
    };

    // Then bundle that value into the returned enum type.
    RelatedishThings::ReusedStructure(s)
}

/// Shows how the result of an enum comes back as increasingly complex data.
///
/// Note that while we *could* have a numeric value associated with each
/// enumerated value (as in a C/C++ `enum`), we don't *have* to. The value of
/// an enumeration in Rust is *which enumerated value it is*, and nothing more
/// unless you specify something more.
pub fn demonstrate_basic_enumeration() {
    print!(
        "{:?}\n{:?}\n{:?}\n{:?}\n{:?}\n",
        get_unit(),
        get_name(),
        get_value(),
        get_complex_data(),
        get_reused_structure()
    );
}

/// Shows in a bit more detail how `match` works.
///
/// Note that the pattern matches include matching against numbers, tuple types,
/// and more. Big takeaways:
///
///   - You can match against all sorts of types.
///   - You have to match exhaustively. Handle "everything else" with `_` as the
///     sole component of the match arm.
///   - You can destructure complex types into their components. You can ignore
///     components of complex types with `_` as well.
#[cfg_attr(feature = "clippy", allow(approx_constant))]
pub fn demonstrate_match() {
    // You can match on numbers...
    let answer = 42;
    let question = match answer {
        // That includes individual numbers or ranges.
        0 => "What do you get when you divide by this? PROBLEMS.",
        1..=41 => "This is all pretty normal, right?",
        42 => "Life, the universe, and everything, eh? (Canadian version)",
        // What about catching *everything else*? Use `_`.
        _ => "I've got nothing useful to say here.",
    };

    println!("I asked: '{:}'; the answer was: {:}", question, answer);

    // or letters...
    let character = 'C';
    match character {
        'A'..='B' => println!("Nope, not those letters."),
        'C' => println!("Why, yes, my name *does* start with a 'C'"),
        'D'..='z' => println!("None of those either."),
        _ => println!("That's not even a letter!"),
    }

    // or more complex types...
    const THREE: u8 = 3;
    const TWO: u8 = 2;
    let some_tuple = (THREE, TWO);
    match some_tuple {
        // You can match on the full tuple with specific values.
        (THREE, TWO) => println!("Got both!"),
        // You can match on only part of it; note that these match the cases where both are *not*
        // filled with the value specified in the first arm.
        (THREE, _) => println!("Got three only."),
        (_, TWO) => println!("Got two only."),
        // You can also destructure the elements of the tuple into values to be
        // used in the body of the expression, not just ignore them with `_`.
        (a, b) => println!("Got weird other stuff: {:}, {:}", a, b),
    }
}

/// Shows how this is used in a more meaningful context, with a standard type.
#[cfg_attr(feature = "clippy", allow(approx_constant))]
pub fn get_an_option(get_it: bool) -> Option<f64> {
    if get_it {
        // Returns an `Option` enum in the `Some` type.
        Some(std::f64::consts::PI)
    } else {
        // Returns an `Option` enum in the `None` type. This might look like the
        // `null` type you'd see elsewhere; it isn't. See below.
        None
    }
}

/// Shows how an option type works in practice.
pub fn demonstrate_option() {
    // Just demonstrate how it gets printed.
    let some = get_an_option(true);
    let none = get_an_option(false);
    print!("{:?}\n{:?}\n", some, none);

    // You can see, from printing the values above, that the `some` is (as you
    // would expect from seeing how enums work in general) not a plain value;
    // it's wrapped in `Some`! However, we can unwrap it to get at the actual
    // value:
    let some_value = some.unwrap();
    println!("{:?}", some_value);
}

/// Shows how to return either a meaningful result or an error as an enum.
///
/// The `Result` type is a standard pattern for handling cases where the result
/// of a given function may be an error. No more of the C/C++ pattern of passing
/// in/out arguments so that you can get a normal/error-indicating return code.
/// Instead, you just return a `Result`, and then `match` on that value to
/// handle it.
#[cfg_attr(feature = "cargo-clippy", allow(clippy::approx_constant))]
pub fn get_a_result(succeeds: bool) -> Result<f64, String> {
    if succeeds {
        Ok(2.718_281_828)
    } else {
        Err("Huh. This didn't go as planned.".to_string())
    }
}

/// Shows how a `Result` type works in practice.
///
/// A `Result` is an `enum` (which can be converted to the `Option` type), which
/// lets us hand back, and then handle, the results of a given function easily.
pub fn demonstrate_result() {
    // First of all, what is the result of this `match`? What would it be if we
    // changed the call to `get_a_result(false)` instead?
    match get_a_result(true) {
        Ok(value) => println!("The value was: {:}", value),
        Err(message) => println!("ERROR: {:}", message),
    }

    // Note that you can readily convert the `Ok` and `Err` terms in a `Result`
    // to `Option`s!
    let an_ok = get_a_result(true);
    let an_err = get_a_result(false);
    print!("{:?}\n{:?}\n", an_ok.ok(), an_err.err());

    // Let's demonstrate the flip-side (i.e. the `None` and `Some` bits). Note
    // how the methods behave: `Result::ok()` -> `Some` for the `Ok` enum and
    // `None` for the `Err` enum; and `Result::err()` does the inverse..
    let another_ok = get_a_result(true);
    let another_err = get_a_result(false);
    print!("{:?}\n{:?}\n", another_ok.err(), another_err.ok());
}