Values
values.zig
// Top-level declarations are order-independent:
const print = std.debug.print;
const std = @import("std");
const os = std.os;
const assert = std.debug.assert;
pub fn main() void {
// integers
const one_plus_one: i32 = 1 + 1;
print("1 + 1 = {}\n", .{one_plus_one});
// floats
const seven_div_three: f32 = 7.0 / 3.0;
print("7.0 / 3.0 = {}\n", .{seven_div_three});
// boolean
print("{}\n{}\n{}\n", .{
true and false,
true or false,
!true,
});
// optional
var optional_value: ?[]const u8 = null;
assert(optional_value == null);
print("\noptional 1\ntype: {}\nvalue: {}\n", .{
@typeName(@TypeOf(optional_value)),
optional_value,
});
optional_value = "hi";
assert(optional_value != null);
print("\noptional 2\ntype: {}\nvalue: {}\n", .{
@typeName(@TypeOf(optional_value)),
optional_value,
});
// error union
var number_or_error: anyerror!i32 = error.ArgNotFound;
print("\nerror union 1\ntype: {}\nvalue: {}\n", .{
@typeName(@TypeOf(number_or_error)),
number_or_error,
});
number_or_error = 1234;
print("\nerror union 2\ntype: {}\nvalue: {}\n", .{
@typeName(@TypeOf(number_or_error)),
number_or_error,
});
}
$ zig build-exe values.zig
$ ./values
1 + 1 = 2
7.0 / 3.0 = 2.33333325e+00
false
true
false
optional 1
type: ?[]const u8
value: null
optional 2
type: ?[]const u8
value: hi
error union 1
type: anyerror!i32
value: error.ArgNotFound
error union 2
type: anyerror!i32
value: 1234
Primitive Types
Name | C Equivalent | Description |
---|---|---|
i8 | int8_t | signed 8-bit integer |
u8 | uint8_t | unsigned 8-bit integer |
i16 | int16_t | signed 16-bit integer |
u16 | uint16_t | unsigned 16-bit integer |
i32 | int32_t | signed 32-bit integer |
u32 | uint32_t | unsigned 32-bit integer |
i64 | int64_t | signed 64-bit integer |
u64 | uint64_t | unsigned 64-bit integer |
i128 | int128 | signed 128-bit integer |
u128 | unsigned int128 | unsigned 128-bit integer |
isize | intptr_t | signed pointer sized integer |
usize | uintptr_t | unsigned pointer sized integer |
c_short | short | for ABI compatibility with C |
c_ushort | unsigned short | for ABI compatibility with C |
c_int | int | for ABI compatibility with C |
c_uint | unsigned int | for ABI compatibility with C |
c_long | long | for ABI compatibility with C |
c_ulong | unsigned long | for ABI compatibility with C |
c_longlong | long long | for ABI compatibility with C |
c_ulonglong | unsigned long long | for ABI compatibility with C |
c_longdouble | long double | for ABI compatibility with C |
c_void | void | for ABI compatibility with C |
f16 | _Float16 | 16-bit floating point (10-bit mantissa) IEEE-754-2008 binary16 |
f32 | float | 32-bit floating point (23-bit mantissa) IEEE-754-2008 binary32 |
f64 | double | 64-bit floating point (52-bit mantissa) IEEE-754-2008 binary64 |
f128 | _Float128 | 128-bit floating point (112-bit mantissa) IEEE-754-2008 binary128 |
bool | bool | true or false |
void | (none) | 0 bit type |
noreturn | (none) | the type of break , continue , return , unreachable , and while (true) {} |
type | (none) | the type of types |
anyerror | (none) | an error code |
comptime_int | (none) | Only allowed for comptime-known values. The type of integer literals. |
comptime_float | (none) | Only allowed for comptime-known values. The type of float literals. |
In addition to the integer types above, arbitrary bit-width integers can be referenced by using an identifier of i
or u
followed by digits. For example, the identifier i7
refers to a signed 7-bit integer. The maximum allowed bit-width of an integer type is 65535
.
See also:
Primitive Values
Name | Description |
---|---|
true and false | bool values |
null | used to set an optional type to null |
undefined | used to leave a value unspecified |
See also:
String Literals and Character Literals
String literals are single-item constant Pointers to null-terminated UTF-8 encoded byte arrays. The type of string literals encodes both the length, and the fact that they are null-terminated, and thus they can be coerced to both Slices and Null-Terminated Pointers. Dereferencing string literals converts them to Arrays.
Character literals have type comptime_int
, the same as Integer Literals. All Escape Sequences are valid in both string literals and character literals.
test.zig
const expect = @import("std").testing.expect;
const mem = @import("std").mem;
test "string literals" {
const bytes = "hello";
expect(@TypeOf(bytes) == *const [5:0]u8);
expect(bytes.len == 5);
expect(bytes[1] == 'e');
expect(bytes[5] == 0);
expect('e' == '\x65');
expect('\u{1f4a9}' == 128169);
expect('💯' == 128175);
expect(mem.eql(u8, "hello", "h\x65llo"));
}
$ zig test test.zig
1/1 test "string literals"... OK
All 1 tests passed.
See also:
Escape Sequences
Escape Sequence | Name |
---|---|
\n | Newline |
\r | Carriage Return |
\t | Tab |
\ | Backslash |
\’ | Single Quote |
\” | Double Quote |
\xNN | hexadecimal 8-bit character code (2 digits) |
\u{NNNNNN} | hexadecimal Unicode character code UTF-8 encoded (1 or more digits) |
Note that the maximum valid Unicode point is 0x10ffff
.
Multiline String Literals
Multiline string literals have no escapes and can span across multiple lines. To start a multiline string literal, use the \\
token. Just like a comment, the string literal goes until the end of the line. The end of the line is not included in the string literal. However, if the next line begins with \\
then a newline is appended and the string literal continues.
const hello_world_in_c =
\\#include <stdio.h>
\\
\\int main(int argc, char **argv) {
\\ printf("hello world\n");
\\ return 0;
\\}
;
See also:
Assignment
Use the const
keyword to assign a value to an identifier:
test.zig
const x = 1234;
fn foo() void {
// It works at global scope as well as inside functions.
const y = 5678;
// Once assigned, an identifier cannot be changed.
y += 1;
}
test "assignment" {
foo();
}
$ zig test test.zig
./docgen_tmp/test.zig:8:7: error: cannot assign to constant
y += 1;
^
const
applies to all of the bytes that the identifier immediately addresses. Pointers have their own const-ness.
If you need a variable that you can modify, use the var
keyword:
test.zig
const expect = @import("std").testing.expect;
test "var" {
var y: i32 = 5678;
y += 1;
expect(y == 5679);
}
$ zig test test.zig
1/1 test "var"... OK
All 1 tests passed.
Variables must be initialized:
test.zig
test "initialization" {
var x: i32;
x = 1;
}
$ zig test test.zig
./docgen_tmp/test.zig:2:5: error: variables must be initialized
var x: i32;
^
undefined
Use undefined
to leave variables uninitialized:
test.zig
const expect = @import("std").testing.expect;
test "init with undefined" {
var x: i32 = undefined;
x = 1;
expect(x == 1);
}
$ zig test test.zig
1/1 test "init with undefined"... OK
All 1 tests passed.
undefined
can be coerced to any type. Once this happens, it is no longer possible to detect that the value is undefined
. undefined
means the value could be anything, even something that is nonsense according to the type. Translated into English, undefined
means “Not a meaningful value. Using this value would be a bug. The value will be unused, or overwritten before being used.”
In Debug mode, Zig writes 0xaa
bytes to undefined memory. This is to catch bugs early, and to help detect use of undefined memory in a debugger.