about summary refs log tree commit diff
path: root/lang/zig/fn.zig
blob: 0fb3002581ef40f5e3b5a98b502e1b735b7fb634 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
const expect = @import("std").testing.expect;

// Functions are declared like this
fn add(a: i8, b: i8) i8 {
    return if (a == 0) b else a + b;
}

// The export specifier makes a function externally visible in the generated
// object file, and makes it use the C ABI.
export fn sub(a: i8, b: i8) i8 { return a - b; }

// The extern specifier is used to declare a function that will be resolved
// at link time, when linking statically, or at runtime, when linking
// dynamically.
extern "c" fn atan2(a: f64, b: f64) f64;

// The @setCold builtin tells the optimizer that a function is rarely called.
fn abort() noreturn {
    @setCold(true);
    while (true) {}
}

// The naked calling convention makes a function not have any function prologue
// or epilogue.  This can be useful when integrating with assembly.
fn _start() callconv(.Naked) noreturn {
    abort();
}

// The inline specifier forces a function to be inlined at all call sites.
// If the function cannot be inlined, it is a compile-time error.
inline fn shiftLeftOne(a: u32) u32 {
    return a << 1;
}

// The pub specifier allows the function to be visible when importing.
// Another file can use @import and call sub2
pub fn sub2(a: i8, b: i8) i8 { return a - b; }

// Functions can be used as values and are equivalent to pointers.
fn do_op(fn_call: fn (a: i8, b: i8) i8, op1: i8, op2: i8) i8 {
    return fn_call(op1, op2);
}

test "function" {
    expect(do_op(add, 5, 6) == 11);
    expect(do_op(sub2, 5, 6) == -1);
}

const Point = struct { x: i32, y: i32 };

fn foo(point: Point) i32 {
    // Here, `point` could be a reference, or a copy. The function body
    // can ignore the difference and treat it as a value. Be very careful
    // taking the address of the parameter - it should be treated as if
    // the address will become invalid when the function returns.
    return point.x + point.y;
}

test "pass struct to function" {
    expect(foo(Point{ .x = 1, .y = 2 }) == 3);
}

fn addFortyTwo(x: anytype) @TypeOf(x) {
    return x + 42;
}

test "fn type inference" {
    expect(addFortyTwo(1) == 43);
    expect(@TypeOf(addFortyTwo(1)) == comptime_int);
    const y: i64 = 2;
    expect(addFortyTwo(y) == 44);
    expect(@TypeOf(addFortyTwo(y)) == i64);
}

test "fn reflection" {
    expect(@typeInfo(@TypeOf(expect)).Fn.return_type.? == void);
    expect(@typeInfo(@TypeOf(expect)).Fn.is_var_args == false);
}