Browse Source

add ex 55 unions

Dave Gauer 4 years ago
parent
commit
7b1c9c3b63
4 changed files with 87 additions and 1 deletions
  1. 1 1
      README.md
  2. 4 0
      build.zig
  3. 76 0
      exercises/55_unions.zig
  4. 6 0
      patches/patches/55_unions.patch

+ 1 - 1
README.md

@@ -130,7 +130,7 @@ Planned exercises:
 * [x] Struct methods
 * [x] Slices
 * [x] Many pointers
-* [ ] Unions
+* [x] Unions
 * [ ] Numeric types (integers, floats)
 * [ ] Labelled blocks and loops
 * [ ] Loops as expressions

+ 4 - 0
build.zig

@@ -280,6 +280,10 @@ const exercises = [_]Exercise{
         .main_file = "54_manypointers.zig",
         .output = "Memory is a resource.",
     },
+    .{
+        .main_file = "55_unions.zig",
+        .output = "Insect report! Ant alive is: true. Bee visited 15 flowers.",
+    },
 };
 
 /// Check the zig version to make sure it can compile the examples properly.

+ 76 - 0
exercises/55_unions.zig

@@ -0,0 +1,76 @@
+//
+// A union lets you store different types and sizes of data at
+// the same memory address. How is this possible? The compiler
+// sets aside enough memory for the largest thing you might want
+// to store.
+//
+// In this example, an instance of Foo always takes up u64 of
+// space memory even if you're currently storing a u8.
+//
+//     const Foo = union {
+//         small: u8,
+//         medium: u32,
+//         large: u64,
+//     };
+//
+// The syntax looks just like a struct, but a Foo can only hold a
+// small OR a medium OR a large value. Once a field becomes
+// active, the other inactive fields cannot be accessed. To
+// change active fields, assign a whole new instance:
+//
+//     var f = Foo{ .small = 5 };
+//     f.small += 5;                  // OKAY
+//     f.medium = 5432;               // ERROR!
+//     f = Foo{ .medium = 5432 };     // OKAY
+//
+// Unions can save space in memory because they let you "re-use"
+// a space in memory. They also provide a sort of primitive
+// polymorphism. Here fooBar() can take a Foo no matter what size
+// of unsigned integer it holds:
+//
+//     fn fooBar(f: Foo) void { ... }
+//
+// Oh, but how does fooBar() know which field is active? Zig has
+// a neat way of keeping track, but for now, we'll just have to
+// do it manually.
+//
+// Let's see if we can get this program working!
+//
+const std = @import("std");
+
+// We've just started writing a simple ecosystem simulation.
+// Insects will be represented by either bees or ants. Bees store
+// the number of flowers they've visited that day and ants just
+// store whether or not they're still alive.
+const Insect = union {
+    flowers_visited: u16,
+    still_alive: bool,
+};
+
+// Since we need to specify the type of insect, we'll use an
+// enum (remember those?).
+const AntOrBee = enum { a, b };
+
+pub fn main() void {
+    // We'll just make one bee and one ant to test them out:
+    var ant = Insect{ .still_alive = true };
+    var bee = Insect{ .flowers_visited = 15 };
+
+    std.debug.print("Insect report! ", .{});
+
+    // Oops! We've made a mistake here.
+    printInsect(ant, AntOrBee.c);
+    printInsect(bee, AntOrBee.c);
+
+    std.debug.print("\n", .{});
+}
+
+// Eccentric Doctor Zoraptera says that we can only use one
+// function to print our insects. Doctor Z is small and sometimes
+// inscrutable but we do not question her.
+fn printInsect(insect: Insect, what_it_is: AntOrBee) void {
+    switch (what_it_is) {
+        .a => std.debug.print("Ant alive is: {}. ", .{insect.still_alive}),
+        .b => std.debug.print("Bee visited {} flowers. ", .{insect.flowers_visited}),
+    }
+}

+ 6 - 0
patches/patches/55_unions.patch

@@ -0,0 +1,6 @@
+62,63c62,63
+<     printInsect(ant, AntOrBee.c);
+<     printInsect(bee, AntOrBee.c);
+---
+>     printInsect(ant, AntOrBee.a);
+>     printInsect(bee, AntOrBee.b);