Casting

    An implicit cast occurs when one type is expected, but different type is provided:

    test.zig

    1. 1/3 test "implicit cast - variable declaration"...OK
    2. 2/3 test "implicit cast - function call"...OK
    3. 3/3 test "implicit cast - invoke a type as a function"...OK
    4. All tests passed.

    Implicit casts are only allowed when it is completely unambiguous how to get from one type to another, and the transformation is guaranteed to be safe. There is one exception, which is .

    Values which have the same representation at runtime can be cast to increase the strictness of the qualifiers, no matter how nested the qualifiers are:

    • const - non-const to const is allowed
    • volatile - non-volatile to volatile is allowed
    • align - bigger to smaller alignment is allowed
    • error sets to supersets is allowed

    These casts are no-ops at runtime since the value representation does not change.

    test.zig

    1. test "implicit cast - const qualification" {
    2. var a: i32 = 1;
    3. var b: *i32 = &a;
    4. foo(b);
    5. }
    6. fn foo(a: *const i32) void {}
    1. $ zig test test.zig
    2. 1/1 test "implicit cast - const qualification"...OK
    3. All tests passed.

    In addition, pointers implicitly cast to const optional pointers:

    test.zig

    1. const std = @import("std");
    2. const assert = std.debug.assert;
    3. const mem = std.mem;
    4. test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
    5. const window_name = [1][*]const u8{c"window name"};
    6. const x: [*]const ?[*]const u8 = &window_name;
    7. assert(mem.eql(u8, std.mem.toSliceConst(u8, x[0].?), "window name"));
    8. }
    1. $ zig test test.zig
    2. 1/1 test "cast *[1][*]const u8 to [*]const ?[*]const u8"...OK
    3. All tests passed.

    Implicit Cast: Integer and Float Widening

    implicitly cast to integer types which can represent every value of the old type, and likewise Floats implicitly cast to float types which can represent every value of the old type.

    test.zig

    1. const std = @import("std");
    2. const assert = std.debug.assert;
    3. const mem = std.mem;
    4. test "integer widening" {
    5. var a: u8 = 250;
    6. var b: u16 = a;
    7. var c: u32 = b;
    8. var d: u64 = c;
    9. var e: u64 = d;
    10. var f: u128 = e;
    11. assert(f == a);
    12. }
    13. test "implicit unsigned integer to signed integer" {
    14. var a: u8 = 250;
    15. var b: i16 = a;
    16. assert(b == 250);
    17. }
    18. test "float widening" {
    19. var a: f16 = 12.34;
    20. var b: f32 = a;
    21. var c: f64 = b;
    22. var d: f128 = c;
    23. assert(d == a);
    24. }
    1. $ zig test test.zig
    2. 1/3 test "integer widening"...OK
    3. 2/3 test "implicit unsigned integer to signed integer"...OK
    4. 3/3 test "float widening"...OK
    5. All tests passed.

    Implicit Cast: Arrays and Pointers

    1. $ zig test test.zig
    2. 1/7 test "[N]T to []const T"...OK
    3. 2/7 test "[N]T to E![]const T"...OK
    4. 3/7 test "[N]T to ?[]const T"...OK
    5. 4/7 test "*[N]T to []T"...OK
    6. 5/7 test "*[N]T to [*]T"...OK
    7. 6/7 test "*[N]T to ?[*]T"...OK
    8. 7/7 test "*T to *[1]T"...OK
    9. All tests passed.

    See also:

    The payload type of Optionals, as well as , implicitly cast to the optional type.

    test.zig

    1. const std = @import("std");
    2. const assert = std.debug.assert;
    3. test "implicit casting to optionals" {
    4. const x: ?i32 = 1234;
    5. const y: ?i32 = null;
    6. assert(x.? == 1234);
    7. assert(y == null);
    8. }
    1. 1/1 test "implicit casting to optionals"...OK

    It works nested inside the Error Union Type, too:

    test.zig

    1. const std = @import("std");
    2. const assert = std.debug.assert;
    3. test "implicit casting to optionals wrapped in error union" {
    4. const x: anyerror!?i32 = 1234;
    5. const y: anyerror!?i32 = null;
    6. assert((try x).? == 1234);
    7. assert((try y) == null);
    8. }
    1. $ zig test test.zig
    2. 1/1 test "implicit casting to optionals wrapped in error union"...OK
    3. All tests passed.

    Implicit Cast: Error Unions

    The payload type of an as well as the Error Set Type implicitly cast to the error union type:

    test.zig

    1. const std = @import("std");
    2. const assert = std.debug.assert;
    3. test "implicit casting to error unions" {
    4. const x: anyerror!i32 = 1234;
    5. const y: anyerror!i32 = error.Failure;
    6. assert((try x) == 1234);
    7. std.testing.expectError(error.Failure, y);
    8. }
    1. $ zig test test.zig
    2. 1/1 test "implicit casting to error unions"...OK
    3. All tests passed.

    Implicit Cast: Compile-Time Known Numbers

    When a number is -known to be representable in the destination type, it may be implicitly casted:

    test.zig

    1. $ zig test test.zig
    2. 1/1 test "implicit casting large integer type to smaller one when value is comptime known to fit"...OK
    3. All tests passed.

    Tagged unions can be implicitly cast to enums, and enums can be implicitly casted to tagged unions when they are comptime-known to be a field of the union that has only one possible value, such as :

    1. const std = @import("std");
    2. const assert = std.debug.assert;
    3. const E = enum {
    4. One,
    5. Two,
    6. Three,
    7. };
    8. const U = union(E) {
    9. One: i32,
    10. Two: f32,
    11. Three,
    12. };
    13. test "implicit casting between unions and enums" {
    14. var u = U{ .Two = 12.34 };
    15. var e: E = u;
    16. assert(e == E.Two);
    17. const three = E.Three;
    18. var another_u: U = three;
    19. assert(another_u == E.Three);
    20. }
    1. $ zig test test.zig
    2. 1/1 test "implicit casting between unions and enums"...OK
    3. All tests passed.

    See also:

    Implicit Cast: Zero Bit Types

    Zero Bit Types may be implicitly casted to single-item , regardless of const.

    TODO document the reasoning for this

    TODO document whether vice versa should work and why

    test.zig

    1. test "implicit casting of zero bit types" {
    2. var x: void = {};
    3. var y: *void = x;
    4. //var z: void = y; // TODO
    5. }
    1. $ zig test test.zig
    2. 1/1 test "implicit casting of zero bit types"...OK
    3. All tests passed.

    Implicit Cast: undefined

    undefined can be cast to any type.

    Explicit casts are performed via . Some explicit casts are safe; some are not. Some explicit casts perform language-level assertions; some do not. Some explicit casts are no-ops at runtime; some are not.

    • @bitCast - change type but maintain bit representation
    • - make a pointer have more alignment
    • @boolToInt - convert true to 1 and false to 0
    • - convert a slice of bytes to a slice of another type
    • @enumToInt - obtain the integer tag value of an enum or tagged union
    • - convert to a smaller error set
    • @errorToInt - obtain the integer value of an error code
    • - convert a larger float to a smaller float
    • @floatToInt - obtain the integer part of a float value
    • - convert between integer types
    • @intToEnum - obtain an enum value based on its integer tag value
    • - obtain an error code based on its integer value
    • @intToFloat - convert an integer to a float value
    • - convert an address to a pointer
    • @ptrCast - convert between pointer types
    • - obtain the address of a pointer
    • @sliceToBytes - convert a slice of anything to a slice of bytes
    • - convert between integer types, chopping off bits

    Peer Type Resolution occurs in these places:

    This kind of type resolution chooses a type that all peer types can implicitly cast into. Here are some examples:

    test.zig

    1. const std = @import("std");
    2. const assert = std.debug.assert;
    3. const mem = std.mem;
    4. test "peer resolve int widening" {
    5. var a: i8 = 12;
    6. var b: i16 = 34;
    7. var c = a + b;
    8. assert(c == 46);
    9. assert(@typeOf(c) == i16);
    10. }
    11. test "peer resolve arrays of different size to const slice" {
    12. assert(mem.eql(u8, boolToStr(true), "true"));
    13. assert(mem.eql(u8, boolToStr(false), "false"));
    14. comptime assert(mem.eql(u8, boolToStr(true), "true"));
    15. comptime assert(mem.eql(u8, boolToStr(false), "false"));
    16. }
    17. return if (b) "true" else "false";
    18. }
    19. test "peer resolve array and const slice" {
    20. testPeerResolveArrayConstSlice(true);
    21. comptime testPeerResolveArrayConstSlice(true);
    22. }
    23. fn testPeerResolveArrayConstSlice(b: bool) void {
    24. const value1 = if (b) "aoeu" else ([]const u8)("zz");
    25. const value2 = if (b) ([]const u8)("zz") else "aoeu";
    26. assert(mem.eql(u8, value1, "aoeu"));
    27. assert(mem.eql(u8, value2, "zz"));
    28. }
    29. test "peer type resolution: ?T and T" {
    30. assert(peerTypeTAndOptionalT(true, false).? == 0);
    31. assert(peerTypeTAndOptionalT(false, false).? == 3);
    32. comptime {
    33. assert(peerTypeTAndOptionalT(true, false).? == 0);
    34. assert(peerTypeTAndOptionalT(false, false).? == 3);
    35. }
    36. }
    37. fn peerTypeTAndOptionalT(c: bool, b: bool) ?usize {
    38. if (c) {
    39. return if (b) null else usize(0);
    40. }
    41. return usize(3);
    42. }
    43. test "peer type resolution: [0]u8 and []const u8" {
    44. assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
    45. assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
    46. comptime {
    47. assert(peerTypeEmptyArrayAndSlice(true, "hi").len == 0);
    48. assert(peerTypeEmptyArrayAndSlice(false, "hi").len == 1);
    49. }
    50. }
    51. fn peerTypeEmptyArrayAndSlice(a: bool, slice: []const u8) []const u8 {
    52. if (a) {
    53. return [_]u8{};
    54. }
    55. return slice[0..1];
    56. }
    57. test "peer type resolution: [0]u8, []const u8, and anyerror![]u8" {
    58. {
    59. var data = "hi";
    60. const slice = data[0..];
    61. assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0);
    62. assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
    63. }
    64. comptime {
    65. var data = "hi";
    66. const slice = data[0..];
    67. assert((try peerTypeEmptyArrayAndSliceAndError(true, slice)).len == 0);
    68. assert((try peerTypeEmptyArrayAndSliceAndError(false, slice)).len == 1);
    69. }
    70. }
    71. fn peerTypeEmptyArrayAndSliceAndError(a: bool, slice: []u8) anyerror![]u8 {
    72. if (a) {
    73. return [_]u8{};
    74. }
    75. return slice[0..1];
    76. }
    77. test "peer type resolution: *const T and ?*T" {
    78. const a = @intToPtr(*const usize, 0x123456789);
    79. const b = @intToPtr(?*usize, 0x123456789);
    80. assert(a == b);
    81. assert(b == a);
    82. }
    1. $ zig test test.zig
    2. 1/7 test "peer resolve int widening"...OK
    3. 2/7 test "peer resolve arrays of different size to const slice"...OK
    4. 3/7 test "peer resolve array and const slice"...OK
    5. 4/7 test "peer type resolution: ?T and T"...OK
    6. 5/7 test "peer type resolution: [0]u8 and []const u8"...OK
    7. 6/7 test "peer type resolution: [0]u8, []const u8, and anyerror![]u8"...OK
    8. All tests passed.