オフィスアワーがそろそろ始まるよ!()

関数

Zenの関数は、他のプログラミング言語と似ています。本書内でも、これまでにいくつかの関数が出てきました。そのうちの1つがmain関数です。main関数はOS上で動くZenアプリケーションで最初に実行する関数です。

pub fn main() anyerror!void {
    std.debug.warn("Hello World.\n");
}

Zenの関数はキャメルケースで命名することが慣習になっています。キャメルケースとは、単語の区切りを大文字にする記述方法です。例を見てみましょう。

fn doSomething() void {
    std.debug.warn("do something!\n");
}

関数の定義

関数はファイルのルートスコープ、構造体の中、列挙型の中、共用体の中、タグ付き共用体の中、に定義することができます。関数の定義は、fnキーワードから開始し、関数名、引数、戻り値型、本体ブロックと続きます。fnキーワードの前に可視性を表すpubをつけることもできます。

pub fn 関数名(引数: 引数型, ... ) 戻り値型 {
    本体ブロック
}

上記の基本構文に加えて、必要に応じて、関数の呼出規約 (calling convension) 、バイトアライメント、リンクセクション、を指定することができます。

可視性 呼出規約 fn(引数: 引数型, ... ) バイトアライメント リンクセクション 戻り値型 {
    本体ブロック
}

全てを指定すると、次のような関数宣言になります。

pub extern fn reset() align(8) linksection(".vector_table.reset_vector") void {
    // ...
}

引数がなく戻り値もない (void) 関数の定義例を示します。Zenでは戻り値がない場合でも、戻り値型の記述を省略することはできないことに注意して下さい。

fn doNothing() void {}

関数を呼び出す

定義した関数は、関数名()で呼び出すことができます。

fn callDoNothing() void {
    doNothing();
}

関数の定義 (もしくは宣言) は、関数呼び出しの後ろにあってもかまいません。Zenではビルド対象となるソースファイル内のどこかで関数が定義されていれば、その関数を呼び出すことができます。

fn callAnother() void {
    anotherFunction();
}

fn anotherFunction() void {}

引数

ノート: 本書内では、仮引数 (parameter) と実引数 (argument)を特別な意図がない限り、どちらも引数と呼びます。

関数は引数を受け取ることができます。u32の引数を受け取る関数は、次のように定義します。Zenでは引数の型を必ず書かなければいけません。

fn printValue(a: u32) void {
    std.debug.warn("value is {}\n", a);
}

この関数を呼び出すコードは次の通りです。関数を呼び出すときに、実際の値を与えることができます。

    printValue(5);

実行結果は次のとおりです。

value is 5

引数が渡される時、整数型浮動小数点型の値は常にコピーされ、関数はその値のコピーを受け取ります。次の受け取った引数のアドレスを返す関数を使って、値がコピーされていることを確認してみましょう。

examples/ch04-basic-syntax/src/function.zen:44:46

fn primitiveArgument(x: u32) usize {
    return @ptrToInt(&x);
}

次のテストで呼び出し側と呼び出された側のアドレスが一致していないことをテストします。ok(caller != callee);のテストは無事パスします。

examples/ch04-basic-syntax/src/function.zen:48:54

test "primitive arguments are copied" {
    var x: u32 = 42;
    const caller = @ptrToInt(&x);
    const callee = primitiveArgument(x);

    ok(caller != callee);
}

サイズの大きい配列列挙型構造体共用体は、コピー動作が高価になるため、参照として関数に渡すのが効率的です。これらの型が引数として渡される場合、Zenコンパイラは、引数のコピーを渡すか参照として渡すか、動作が高速な方を選択します。

次のコードを使って検証してみましょう。bigSizeArgument関数の引数型は[10]u32です。この配列の値をコピーするより、参照を渡すほうが効率的でしょう。

examples/ch04-basic-syntax/src/function.zen:56:58

fn bigSizeArgument(x: [10]u32) usize {
    return @ptrToInt(&x);
}

次のテストで、呼び出し側と呼び出された側とで、配列のアドレスが一致することをテストします。ok(caller == callee);のテストは無事パスします。

examples/ch04-basic-syntax/src/function.zen:60:66

test "big size arguments are copied" {
    var x = [_]u32{0} ** 10;
    const caller = @ptrToInt(&x);
    const callee = bigSizeArgument(x);

    ok(caller == callee);
}

関数の引数はイミュータブルであることに注意してください。

fn modifyArgument(a: u32) void {
    a += 1;
}

このコードは、次のコンパイルエラーになります。

error[E02030]: cannot assign to constant variable
    a += 1;
      ~

ノート: 呼出規約がexternに設定されている関数に対しては、C言語のABIに従います。すなわち、構造体と共用体は必ず値渡しします。

戻り値

関数は、自身を呼び出した関数に対して、値を返すことができます。returnキーワードで指定された値が、関数の戻り値となります。

examples/ch04-basic-syntax/src/function.zen:68:75

fn mul(a: u32, b: u32) u32 {
    if (a == 0 or b == 0) {
        // 早期リターン
        // 型は戻り値の型から推論される
        return 0;
    }
    return a * b;
}

呼び出し側の関数では、戻り値を変数に格納して使用することも、そのまま一時的な値として使用することもできます。

examples/ch04-basic-syntax/src/function.zen:77:83

test "return a value" {
    // 戻り値を変数に格納して使用する
    const result = mul(0, 0);
    ok(result == 0);
    // 戻り値を変数に格納せずに使用する
    ok(mul(2, 3) == 6);
}

ただし、戻り値がある関数の戻り値を無視することはできません。

    // 戻り値を使用していない
    mul(3, 4);

上のコードは次のコンパイルエラーになります。

error: expression value is ignored
    mul(3, 4);
       ^

戻り値が不要な場合は、_でその値が不要であることを明示します。

examples/ch04-basic-syntax/src/function.zen:85:87

test "ignore return value" {
    _ = mul(3, 4);
}

戻り値型がvoidの場合、値を指定せずにreturnするか、関数の末尾の文を実行すると、呼び出した関数に制御が戻ります。

examples/ch04-basic-syntax/src/function.zen:89:96

fn returnVoid(a: bool) void {
    if (a) {
        // 早期リターン
        return;
    }
    // 次の`return`は省略可能
    return;
}

エラーを返す

関数からエラーを返す場合は、エラー型を使用します。エラーを返す関数の戻り値型は、エラー型と正常終了時の戻り値型とを合成したエラー共用体です。

例を示します。

examples/ch04-basic-syntax/src/function.zen:103:109

const Error = error {
    AnError,
};

fn returnError() Error!void {
    return Error.AnError;
}

returnError関数の戻り値型はError!voidです。これは、Error型のエラーが返るか、関数が何も返さないことを意味します。

次の例は、Erroru32の値を返す関数です。

examples/ch04-basic-syntax/src/function.zen:117:122

fn returnErrorOrU32(x: u32) Error!u32 {
    if (x == 0) {
        return Error.AnError;
    }
    return x - 1;
}

詳細は3章 エラー型を参照して下さい。

関数の可視性

関数が外部からアクセス可能かどうかを指定することができます。キーワードpubを使って、関数を修飾します。2つの関数の違いは、他のソースコードファイルから関数を呼び出せるかどうか、です。

fn privateFunction() void {}
pub fn publicFunction() void {}

呼び出し規約

呼び出し規約には、以下のキーワードが使用できます。

  • nakedcc
  • stdcallcc
  • extern

☰ 人の生きた証は永遠に残るよう ☰
Copyright © 2018-2019 connectFree Corporation. All rights reserved.
Zen, the Zen three-circles logo and The Zen Programming Language are trademarks of connectFree corporation in Japan and other countries.