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: {?s}\n", .{
@TypeOf(optional_value), optional_value,
});
optional_value = "hi";
assert(optional_value != null);
print("\noptional 2\ntype: {}\nvalue: {?s}\n", .{
@TypeOf(optional_value), optional_value,
});
// error union
var number_or_error: anyerror!i32 = error.ArgNotFound;
print("\nerror union 1\ntype: {}\nvalue: {!}\n", .{
@TypeOf(number_or_error), number_or_error, });
number_or_error = 1234;
print("\nerror union 2\ntype: {}\nvalue: {!}\n", .{
@TypeOf(number_or_error), number_or_error,
});
}
Shell
$ zig build-exe values.zig
$ ./values
1 + 1 = 2
7.0 / 3.0 = 2.3333333e0
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
Type | 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 , size_t | unsigned pointer sized integer. Also see #5185 |
c_char | char | for ABI compatibility with C |
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 |
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 |
f80 | double | 80-bit floating point (64-bit mantissa) IEEE-754-2008 80-bit extended precision |
f128 | _Float128 | 128-bit floating point (112-bit mantissa) IEEE-754-2008 binary128 |
bool | bool | true or false |
anyopaque | void | Used for type-erased pointers. |
void | (none) | Always the value void{} |
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 Unicode Code Point Literals
String literals are constant single-item Pointers to null-terminated 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.
Because Zig source code is UTF-8 encoded, any non-ASCII bytes appearing within a string literal in source code carry their UTF-8 meaning into the content of the string in the Zig program; the bytes are not modified by the compiler. It is possible to embed non-UTF-8 bytes into a string literal using \xNN
notation.
Indexing into a string containing non-ASCII bytes returns individual bytes, whether valid UTF-8 or not.
Unicode code point literals have type comptime_int
, the same as Integer Literals. All Escape Sequences are valid in both string literals and Unicode code point literals.
string_literals.zig
const print = @import("std").debug.print;
const mem = @import("std").mem; // will be used to compare bytes
pub fn main() void {
const bytes = "hello";
print("{}\n", .{@TypeOf(bytes)}); // *const [5:0]u8
print("{d}\n", .{bytes.len}); // 5
print("{c}\n", .{bytes[1]}); // 'e'
print("{d}\n", .{bytes[5]}); // 0
print("{}\n", .{'e' == '\x65'}); // true
print("{d}\n", .{'\u{1f4a9}'}); // 128169
print("{d}\n", .{'💯'}); // 128175
print("{u}\n", .{'⚡'});
print("{}\n", .{mem.eql(u8, "hello", "h\x65llo")}); // true
print("{}\n", .{mem.eql(u8, "💯", "\xf0\x9f\x92\xaf")}); // also true
const invalid_utf8 = "\xff\xfe"; // non-UTF-8 strings are possible with \xNN notation.
print("0x{x}\n", .{invalid_utf8[1]}); // indexing them returns individual bytes...
print("0x{x}\n", .{"💯"[1]}); // ...as does indexing part-way through non-ASCII characters
}
Shell
$ zig build-exe string_literals.zig
$ ./string_literals
*const [5:0]u8
5
e
0
true
128169
128175
⚡
true
true
0xfe
0x9f
See also:
Escape Sequences
Escape Sequence | Name |
---|---|
\n | Newline |
\r | Carriage Return |
\t | Tab |
\ | Backslash |
\’ | Single Quote |
\” | Double Quote |
\xNN | hexadecimal 8-bit byte value (2 digits) |
\u{NNNNNN} | hexadecimal Unicode code point 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.
multiline_string_literals.zig
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:
constant_identifier_cannot_change.zig
const x = 1234;
fn foo() void {
// It works at file scope as well as inside functions.
const y = 5678;
// Once assigned, an identifier cannot be changed.
y += 1;
}
pub fn main() void {
foo();
}
Shell
$ zig build-exe constant_identifier_cannot_change.zig
constant_identifier_cannot_change.zig:8:7: error: cannot assign to constant
y += 1;
~~^~~~
referenced by:
main: constant_identifier_cannot_change.zig:12:5
callMain: /home/ci/actions-runner/_work/zig-bootstrap/out/host/lib/zig/std/start.zig:501:17
remaining reference traces hidden; use '-freference-trace' to see all reference traces
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:
mutable_var.zig
const print = @import("std").debug.print;
pub fn main() void {
var y: i32 = 5678;
y += 1;
print("{d}", .{y});
}
Shell
$ zig build-exe mutable_var.zig
$ ./mutable_var
5679
Variables must be initialized:
var_must_be_initialized.zig
pub fn main() void {
var x: i32;
x = 1;
}
Shell
$ zig build-exe var_must_be_initialized.zig
var_must_be_initialized.zig:2:15: error: expected '=', found ';'
var x: i32;
^
undefined
Use undefined
to leave variables uninitialized:
assign_undefined.zig
const print = @import("std").debug.print;
pub fn main() void {
var x: i32 = undefined;
x = 1;
print("{d}", .{x});
}
Shell
$ zig build-exe assign_undefined.zig
$ ./assign_undefined
1
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. However, this behavior is only an implementation feature, not a language semantic, so it is not guaranteed to be observable to code.