070_comptime5.zig 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. //
  2. // Being able to pass types to functions at compile time lets us
  3. // generate code that works with multiple types. But it doesn't
  4. // help us pass VALUES of different types to a function.
  5. //
  6. // For that, we have the 'anytype' placeholder, which tells Zig
  7. // to infer the actual type of a parameter at compile time.
  8. //
  9. // fn foo(thing: anytype) void { ... }
  10. //
  11. // Then we can use builtins such as @TypeOf(), @typeInfo(),
  12. // @typeName(), @hasDecl(), and @hasField() to determine more
  13. // about the type that has been passed in. All of this logic will
  14. // be performed entirely at compile time.
  15. //
  16. const print = @import("std").debug.print;
  17. // Let's define three structs: Duck, RubberDuck, and Duct. Notice
  18. // that Duck and RubberDuck both contain waddle() and quack()
  19. // methods declared in their namespace (also known as "decls").
  20. const Duck = struct {
  21. eggs: u8,
  22. loudness: u8,
  23. location_x: i32 = 0,
  24. location_y: i32 = 0,
  25. fn waddle(self: Duck, x: i16, y: i16) void {
  26. self.location_x += x;
  27. self.location_y += y;
  28. }
  29. fn quack(self: Duck) void {
  30. if (self.loudness < 4) {
  31. print("\"Quack.\" ", .{});
  32. } else {
  33. print("\"QUACK!\" ", .{});
  34. }
  35. }
  36. };
  37. const RubberDuck = struct {
  38. in_bath: bool = false,
  39. location_x: i32 = 0,
  40. location_y: i32 = 0,
  41. fn waddle(self: RubberDuck, x: i16, y: i16) void {
  42. self.location_x += x;
  43. self.location_y += y;
  44. }
  45. fn quack(self: RubberDuck) void {
  46. // Assigning an expression to '_' allows us to safely
  47. // "use" the value while also ignoring it.
  48. _ = self;
  49. print("\"Squeek!\" ", .{});
  50. }
  51. fn listen(self: RubberDuck, dev_talk: []const u8) void {
  52. // Listen to developer talk about programming problem.
  53. // Silently contemplate problem. Emit helpful sound.
  54. _ = dev_talk;
  55. self.quack();
  56. }
  57. };
  58. const Duct = struct {
  59. diameter: u32,
  60. length: u32,
  61. galvanized: bool,
  62. connection: ?*Duct = null,
  63. fn connect(self: Duct, other: *Duct) !void {
  64. if (self.diameter == other.diameter) {
  65. self.connection = other;
  66. } else {
  67. return DuctError.UnmatchedDiameters;
  68. }
  69. }
  70. };
  71. const DuctError = error{UnmatchedDiameters};
  72. pub fn main() void {
  73. // This is a real duck!
  74. const ducky1 = Duck{
  75. .eggs = 0,
  76. .loudness = 3,
  77. };
  78. // This is not a real duck, but it has quack() and waddle()
  79. // abilities, so it's still a "duck".
  80. const ducky2 = RubberDuck{
  81. .in_bath = false,
  82. };
  83. // This is not even remotely a duck.
  84. const ducky3 = Duct{
  85. .diameter = 17,
  86. .length = 165,
  87. .galvanized = true,
  88. };
  89. print("ducky1: {}, ", .{isADuck(ducky1)});
  90. print("ducky2: {}, ", .{isADuck(ducky2)});
  91. print("ducky3: {}\n", .{isADuck(ducky3)});
  92. }
  93. // This function has a single parameter which is inferred at
  94. // compile time. It uses builtins @TypeOf() and @hasDecl() to
  95. // perform duck typing ("if it walks like a duck and it quacks
  96. // like a duck, then it must be a duck") to determine if the type
  97. // is a "duck".
  98. fn isADuck(possible_duck: anytype) bool {
  99. // We'll use @hasDecl() to determine if the type has
  100. // everything needed to be a "duck".
  101. //
  102. // In this example, 'has_increment' will be true if type Foo
  103. // has an increment() method:
  104. //
  105. // const has_increment = @hasDecl(Foo, "increment");
  106. //
  107. // Please make sure MyType has both waddle() and quack()
  108. // methods:
  109. const MyType = @TypeOf(possible_duck);
  110. const walks_like_duck = ???;
  111. const quacks_like_duck = ???;
  112. const is_duck = walks_like_duck and quacks_like_duck;
  113. if (is_duck) {
  114. // We also call the quack() method here to prove that Zig
  115. // allows us to perform duck actions on anything
  116. // sufficiently duck-like.
  117. //
  118. // Because all of the checking and inference is performed
  119. // at compile time, we still have complete type safety:
  120. // attempting to call the quack() method on a struct that
  121. // doesn't have it (like Duct) would result in a compile
  122. // error, not a runtime panic or crash!
  123. possible_duck.quack();
  124. }
  125. return is_duck;
  126. }