Browse Source

add ex070 comptime 5

Dave Gauer 4 years ago
parent
commit
9c095c60a4
3 changed files with 146 additions and 0 deletions
  1. 5 0
      build.zig
  2. 135 0
      exercises/070_comptime5.zig
  3. 6 0
      patches/patches/070_comptime5.patch

+ 5 - 0
build.zig

@@ -348,6 +348,11 @@ const exercises = [_]Exercise{
         .main_file = "069_comptime4.zig",
         .output = "s1={ 1, 2, 3 }, s2={ 1, 2, 3, 4, 5 }, s3={ 1, 2, 3, 4, 5, 6, 7 }",
     },
+    .{
+        .main_file = "070_comptime5.zig",
+        .output = "\"Quack.\" ducky1: true, \"Squeek!\" ducky2: true, ducky3: false",
+        .hint = "Have you kept the wizard hat on?",
+    },
 };
 
 /// Check the zig version to make sure it can compile the examples properly.

+ 135 - 0
exercises/070_comptime5.zig

@@ -0,0 +1,135 @@
+//
+// Being able to pass types to functions at compile time lets us
+// generate code that works with multiple types. But it doesn't
+// help us pass VALUES of different types to a function.
+//
+// For that, we have the 'anytype' placeholder, which tells Zig
+// to infer the actual type of a parameter at compile time.
+//
+//     fn foo(thing: anytype) void { ... }
+//
+// Then we can use builtins such as @TypeOf(), @typeInfo(),
+// @typeName(), @hasDecl(), and @hasField() to determine more
+// about the type that has been passed in. All of this logic will
+// be performed entirely at compile time.
+//
+const print = @import("std").debug.print;
+
+// Let's define three structs: Duck, RubberDuck, and Duct. Notice
+// that Duck and RubberDuck both contain waddle() and quack()
+// methods declared in their namespace (also known as "decls").
+
+const Duck = struct {
+    eggs: u8,
+    loudness: u8,
+    location_x: i32 = 0,
+    location_y: i32 = 0,
+
+    fn waddle(self: Duck, x: i16, y: i16) void {
+        self.location_x += x;
+        self.location_y += y;
+    }
+
+    fn quack(self: Duck) void {
+        if (self.loudness < 4) {
+            print("\"Quack.\" ", .{});
+        } else {
+            print("\"QUACK!\" ", .{});
+        }
+    }
+};
+
+const RubberDuck = struct {
+    in_bath: bool = false,
+    location_x: i32 = 0,
+    location_y: i32 = 0,
+
+    fn waddle(self: RubberDuck, x: i16, y: i16) void {
+        self.location_x += x;
+        self.location_y += y;
+    }
+
+    fn quack(self: RubberDuck) void {
+        print("\"Squeek!\" ", .{});
+    }
+};
+
+const Duct = struct {
+    diameter: u32,
+    length: u32,
+    galvanized: bool,
+    connection: ?*Duct = null,
+
+    fn connect(self: Duct, other: *Duct) !void {
+        if (self.diameter == other.diameter) {
+            self.connection = other;
+        } else {
+            return DuctError.UnmatchedDiameters;
+        }
+    }
+};
+
+const DuctError = error{UnmatchedDiameters};
+
+pub fn main() void {
+    // This is a real duck!
+    const ducky1 = Duck{
+        .eggs = 0,
+        .loudness = 3,
+    };
+
+    // This is not a real duck, but it has quack() and waddle()
+    // abilities, so it's still a "duck".
+    const ducky2 = RubberDuck{
+        .in_bath = false,
+    };
+
+    // This is not even remotely a duck.
+    const ducky3 = Duct{
+        .diameter = 17,
+        .length = 165,
+        .galvanized = true,
+    };
+
+    print("ducky1: {}, ", .{isADuck(ducky1)});
+    print("ducky2: {}, ", .{isADuck(ducky2)});
+    print("ducky3: {}\n", .{isADuck(ducky3)});
+}
+
+// This function has a single parameter which is inferred at
+// compile time. It uses builtins @TypeOf() and @hasDecl() to
+// perform duck typing ("if it walks like a duck and it quacks
+// like a duck, then it must be a duck") to determine if the type
+// is a "duck".
+fn isADuck(possible_duck: anytype) bool {
+    // We'll use @hasDecl() to determine if the type has
+    // everything needed to be a "duck".
+    //
+    // In this example, 'has_increment' will be true if type Foo
+    // has an increment() method:
+    //
+    //     const has_increment = @hasDecl(Foo, "increment");
+    //
+    // Please make sure MyType has both waddle() and quack()
+    // methods:
+    const MyType = @TypeOf(possible_duck);
+    const walks_like_duck = ???;
+    const quacks_like_duck = ???;
+
+    const is_duck = walks_like_duck and quacks_like_duck;
+
+    if (is_duck) {
+        // We also call the quack() method here to prove that Zig
+        // allows us to perform duck actions on anything
+        // sufficiently duck-like.
+        //
+        // Because all of the checking and inference is performed
+        // at compile time, we still have complete type safety:
+        // attempting to call the quack() method on a struct that
+        // doesn't have it (like Duct) would result in a compile
+        // error, not a runtime panic or crash!
+        possible_duck.quack();
+    }
+        
+    return is_duck;
+}

+ 6 - 0
patches/patches/070_comptime5.patch

@@ -0,0 +1,6 @@
+116,117c116,117
+<     const walks_like_duck = ???;
+<     const quacks_like_duck = ???;
+---
+>     const walks_like_duck = @hasDecl(MyType, "waddle");
+>     const quacks_like_duck = @hasDecl(MyType, "quack");