Open17

「Understanding the Odin Programming Language」読書録

zenwerkzenwerk

Hello, World

ビルド

Odin はパッケージ(ディレクトリ)単位でビルドする

ビルドして実行

# カレントディレクトリのパッケージをビルド&実行
odin run .

コンパイル

odin build .

ファイル指定ビルド

-file オプション

odin build foo.odin -file

コンパイルオブション

オプション一覧

odin build -help

Odin推奨のコーディングスタイルを指定してビルド(違反したらエラー)

odin build . -vet -strict-style
zenwerkzenwerk

変数と定数

基本

number: int  // ゼロ値で初期化
number = 7

number := 42 // 型推論付き初期化

定数

コンパイル時定数のこと.

Odinでは変数と定数で型の扱いが異なる.
定数が格納可能な範囲であれば、どんな型の変数にも代入可能.

CONST_NUM :: 128    // `Untyped` Integer
CONST_FLT :: 27.32  // `Untyped` Float

number1 := CONST_NUM        // int と推論されOK
float_num: f32 = CONST_NUM  // f32 と推論されOK

number2: i8 = CONST_NUM   // コンパイルエラー
number3: int = CONST_FLT  // コンパイルエラー

4, 3.14 のようなリテラルも Untyped な定数.
デフォルトで int, f64 に推論される.

型指定定数

Untyped な挙動を抑制したいとき

DECIMAL_CONST: f32: 3.14

Untyped の動作が追加された理由

C言語では 0.5 は 64bit 浮動小数点であり、32bit として扱いたいときは 0.5f と都度明示する必要があった.
Odin では必要になるまで型の決定を遅らせることで余計な手間を取らせないようにした.

zenwerkzenwerk

基本文法(一部)

固定配列

// ゼロ初期化
ten_ints: [10]int

// 初期化つき定義
ten_ints := [10]int {
	61, 81, 12, 41, 5, 10, 1234, 8, 4, 1,
}

無限ループ

for {
	fmt.println("無限ループって怖くね")
}

While風ループ

Odin に While文はない

i := 0
for i < 10 {
	fmt.println(i) // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
	i += 1
}

範囲for

// 開区間 [0, 10)
for i in 0..<10 {
	fmt.println(i) // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
}

// 閉区間 [0, 10]
for i in 0..=10 {
	fmt.println(i) // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
}

固定配列の範囲for

for n in ten_ints {
	fmt.println(n) // 61, 81, 12, 41, 5, 10, 1234, 8, 4, 1
}

// `#reverse` をつけると逆順になる
#reverse for n in ten_ints {
	fmt.println(n) // 1, 4, 8, 1234, 10, 5, 41, 12, 81, 61
}

ラベル付き break

outer: for x in 0..<20 {
	for y in 0..<20 {
		if x == 5 && y == 5 {
			// 外側のループを抜ける
			break outer
		}
	}
}
zenwerkzenwerk

構造体

// 定義
Point :: struct {
	x: int,
	y: int,
}

// 初期化
p1 := Point {
	x = 10,
	y = 20,
}
p2 := Point{30, 40}

構造体はメンバ関数を持てない

Go の構造体メソッド

func (f Foo) some_func() { ... }

のような機能は Odin にはない。
構造体のメンバに func: proc(i: int) -> int などと書けば関数を格納できるが、有効な場面は少ないとのこと。
C言語のように構造体定義と、それを操作する関数をすべて別に書くのが Odin の基本的な作法。

構造体の埋め込み

Point :: struct {
	x: int,
	y: int,
}

Rectangle :: struct {
	point: Point, // 埋め込み
	width: int,
	height: int,
}

// 初期化
r := Rect {
	point = {
		x = 10,
		y = 10,
	},
	width = 100,
	height = 200,
}

r.width = 200
r.point.x = 20
foo := r.point.y

usingキーワード

using で埋め込まれた構造体名を省略可能

Person_Stats :: struct {
	health: int,
	age: int,
}

Person :: struct {
	using stats: Person_Stats, // `stats` を省略してメンバにアクセス可能
	name: string,
}

// アクセス
p := Person {
	health = 20
}
p.age = 70

hs := p.health

using による型の抽象化

using で埋め込まれた構造体型は、アップキャストしなくても関数に渡せる.

Foo :: struct {
    x: int,
}

Bar :: struct {
    using foo: Foo,
    y: int,
}

bar := Bar{x = 10, y = 20}
print_foo :: proc(f: Foo) { fmt.println(f.x) } // Foo を受け取る関数
print_foo(bar) // OK!!
zenwerkzenwerk

Enum (列挙型)

ほぼ Go の iota と同じ

Computer_Type :: enum {
	Laptop = 0,    // value 0 
	Desktop,       // value 1
	Mainframe = 5, // value 5
	Broken,        // value 6
	Gamer_Rig,     // value 7
}

Switchで使う

Switch 文で Enum を使う場合すべての case を定義しないとコンパイルエラー.
#partial を指定すると、網羅しなくてもコンパイルできる.

#partial switch ct {
case .Laptop:  // 型推論が効くので `Computer_Type.` の記述が省略可能
	fmt.println("It's a laptop")
case .Desktop:
	fmt.println("It's a desktop")
}

内部の型

デフォルトで Enum には内部的に int が使われる.
明示すれば変更可能. 他言語のライブラリのバインディングを作るときに使う.

// 内部的に u8 型をつかう
Animal_Type :: enum u8 {
	Cat,
	Rabbit,
}
zenwerkzenwerk

Union 型

C言語の union みたいなやつ.
現在保持している型と、その型の実際のデータの両方を格納する.

My_Union :: union {
	f32,
	int,
	Person_Data,
}

Person_Data :: struct {
	health: int,
	age: int,
}

val: My_Union = int(12)

使用メモリ

Union内部の型は「バリアント」と呼ぶ.
Union型の使用メモリは「バリアント内の最大の型+メモリタグの8byte

メモリタグ: 現在どの型を保持しているかを追跡するための内部データ.

保持している型チェック

f32_val, f32_val_ok := val.(f32)

if f32_val_ok {
	// f32_val is OK to use in here.
}

if 文での書き方

if f32_val, f32_val_ok := val.(f32); f32_val_ok {
	// f32_val is OK to use in here.
}

値を変更したい場合

// `&` を付与すると f32_val がポインタ型となる
if f32_val, ok := &val.(f32); ok {
	f32_val^ = 7 // 参照先を更新
}

Switch文で使う

Enumと若干記法が異なる.

// `in` が必要
// & を使うと case の処理内で値がミュータブルになる
switch &v in val {
case int:
	v = 7
case f32:
	v = 42
case Person_Data:
	v.age = 7
}

Unionのゼロ値

デフォルトで nil.
Union型の宣言時に #no_nil を付与すると、一番最初のバリアントがゼロ値.
ゼロ値が使われるとき、内部の型の値も全てゼロ初期化される.

My_Union :: union #no_nil {
	f32,
	int,
}

val: My_Union // f32

C言語風の raw_union

C言語の union にはメモリタグは存在しない.
これと同じ実装を行いたい場合の方法が #raw_union

// 構造体を使っていることに注意
My_Raw_Union :: struct #raw_union {
	number: int,
	struct_val: My_Struct,
}

a_raw_union: My_Raw_Union

#raw_union は、構造体内のすべてのフィールドをメモリ上の同じ位置から開始させ、重なり合うようにすること.
ただしメモリタグ相当の処理を自分で実装する必要がある.

struct と union を組み合わせる

// 一般的なゲームエンティティ
Entity :: struct {
	position: [2]f32,
	texture: Texture,
	variant: Entity_Variant,
}
// プレイヤー
Entity_Player :: struct {
	can_jump: bool,
}
// ロケット
Entity_Rocket :: struct {
	time_in_space: f32,
}
// Union定義
Entity_Variant :: union {
	Entity_Player,
	Entity_Rocket,
}

使用する例

// プレイヤーエンティティを定義
player_entity := Entity {
	position = starting_position,
	texture = player_graphics,
	variant = Entity_Player {
		can_jump = true,
	}
}
zenwerkzenwerk

Maybe 型

Odin 組み込みの値を持つか持たないかを表す型
内部的に1つのバリアントを持つUnion型を使って実装されている
Odinは多値を返せるので、あまり使われることはない機能。

Maybe :: union($T: typeid) {
	T,
}

使い方

time: Maybe(int)
fmt.println(time) // nil
time = 5
fmt.println(time) // 5

アサーション

.? は型推論によって型表記が省略された記法.

if time_val, time_val_ok := time.?; time_val_ok {
	// Use time_val.
}

t := time.? // nil のとき実行時エラーとなる
zenwerkzenwerk

ポインタ

C言語と異なり、 ^ を使う(Pascal由来)
宣言時 → 型名の先頭に ^
参照時 → 変数名の末尾に ^

ポインタの宣言

num: int = 42
num_ptr: ^int // ポインタ型のゼロ値は nil

num_ptr = &num // アドレスの格納

ポインタの操作

以下はC言語の *num += 1 と等価.

num_ptr^ += 1

構造体ポインタの参照時は ^ を省略してメンバの変更可能

Foo :: struct { int x }

inc_x :: proc(f: ^Foo) {
	f.x += 1  // f^.x と書く必要はない
}

malloc 操作

new 関数を使う

num_p := new(32) 
num_p^ = 42
free(num_p)
Foo :: struct {
        x: int
}
foo := new(Foo)
foo^ = {
        x = 42,
}
free(foo)
zenwerkzenwerk

関数(手続き)

「関数」とは厳密には副作用のない処理を表すが、Odin に副作用がないことを保証する機能はないため「手続き」を表す proc をキーワードにしている。
が、面倒なので引き続き"「関数」と呼ぶ。

基本

  • Go と同じく多値を返せる
  • Go と同じく named return 機能がある
  • Python と同じデフォルト引数、名前付き引数の機能がある
  • C++ の [[nodiscard]] と同じ機能がある
@require_results
divide_and_double :: proc(n: f32, d: f32) -> (f32, bool) { ... }

Odin はクロージャをサポートしない

そういう設計とのこと.

do_stuff :: proc() {
	local_variable: int

	print_message :: proc(msg: string) {
		fmt.println(msg)
		// fmt.println(local_variable) // コンパイルエラー: 外側スコープの変数をキャプチャしない
	}

	print_message("Hellope!")
}

引数は常に const

  • 関数内で引数を更新しようとするとコンパイルエラー.
    • Odin の関数引数は 16 Byte を超えると C++ の const & のようなものが渡される内部動作.
  • ただしポインタを経由した操作はOK.
    • ポインタそのもの(格納されたアドレス)は同様に const なので注意
divide_numbers :: proc(n, d: f32) -> f32 {
	n = n / d // コンパイルエラー
	return n
}

操作したければシャドーイングして関数内だけで有効な変数を定義する

divide_numbers :: proc(n, d: f32) -> f32 {
	n := n // 関数内で有効な変数 `n` を定義
	n = n / d
	return n
}

関数のオーバーロード

proc { ... } でまとめる.
まとめられた関数は引き続き呼び出し可能.

length :: proc {
	length_float2,
	length_float3,
}

length_float2 :: proc(v: [2]f32) -> f32 {
	return math.sqrt(v.x*v.x + v.y*v.y)
}

length_float3 :: proc(v: [3]f32) -> f32 {
	return math.sqrt(v.x*v.x + v.y*v.y + v.z*v.z)
}

init, fini 関数

Go 言語の init() と同じ機能がある

main :: proc() {
	fmt.println("Program running")
}

@init
startup :: proc() {
	// 初期化処理をここに
}

@fini
shutdown :: proc() {
	// 終了処理をここに
}

defer文

Go と同じ

read_file :: proc() {
	f, err := os.open("my_file.txt")
	if err != os.ERROR_NONE {
		// handle error
	}
	defer os.close(f)
}
zenwerkzenwerk

固定長配列

Swizzling糖衣構文

Swizzling と呼ばれるシェーダー言語由来の糖衣構文がある
配列の長さが4以下の場合、x, y, z, w で各要素にアクセスできる.

pos := [4]f32{1, 2, 3, 4}

fmt.println(pos.x, pos.y, pos.z, pos.w) // 1 2 3 4

// 各要素から別の配列を生成する
fmt.println(pos.xxyy, pos.xz, pos.zw) // [1, 1, 2, 2] [1, 3] [3, 4]

配列演算

APL、J言語っぽい機能がある

// 型エイリアス
Vec3 :: [3]f32

pos1 := Vec3{0, 1, 10}
pos2 := Vec3{1 ,-2, 3}

fmt.println(pos1 + pos2) // [1, -1, 13]
fmt.println(pos1 - pos2) // [-1, 3, 7]
fmt.println(pos1 * pos2) // [0, -2, 30]
fmt.println(pos1 / pos2) // [0, -0.5, 3.3333333]

fmt.println(pos1 * 10) // [0, 10, 100]

列挙型配列

列挙型の各要素に合わせた配列が作成可能

Nice_People :: enum {
	Bob,
	Klucke,
	Tim,
}

nice_rating := [Nice_People]int {
	.Bob = 5,
	.Klucke = 7,
	.Tim = 3,
}

fmt.println(nice_rating) // [.Bob = 5, .Klucke = 7, .Tim = 3]

bobs_niceness := nice_rating[.Bob] // 5

// 部分的な初期化
nice_rating := #partial [Nice_People]int {
	.Klucke = 10,
}
zenwerkzenwerk

動的配列

  • メモリ管理が必要
  • 「アロケータ」によって動的にメモリが確保される
    • Odin のデフォルトは context.allocator

基本

dyn_arr: [dynamic]int // 動的配列の定義

append(&dyn_arr, 5) // 要素を動的に追加する

cap 関数でアロケータによって確保されている要素数を確認できる
この要素数を超えて append すると追加でメモリが確保される

メモリ解放

free もあるが、配列には delete しか使わない.

// 変数名をそのまま渡していることに注意
delete(dyn_arr)

delete 後も dyn_arr を再利用したい場合はゼロリセット(dyn_arr = {})する方が良い

配列のリセット

clear(&dyn_arr) // 確保されたメモリ容量は変えず長さだけ 0 になる

shrink(&dyn_arr) で容量を縮小できるが、まず使う場面がないだろう.

要素の削除

順不同削除

早いが削除後の配列の要素の順序が変更されるので注意.

unordered_remove(&dyn_arr, index)

順序保持削除

要素削除後も全体の並び順が保持される.

ordered_remove(&dyn_arr, index)
zenwerkzenwerk

スライス

  • Python のスライス記法とほぼ同じ.
  • 型は []Foo のように記述.
  • スライスは元となった配列とメモリを共有している
    • RDBMSのViewのような感じ.
      • 作成元の配列から特定の範囲のメモリ参照を切り出したものがスライス
      • メモリアドレスを通じてスライスの要素を変更可能
        • スライスの作成元の配列も変更される

配列を扱う関数は引数の型を slice にすると再利用性が増す

固定長配列も、動的配列もスライスを作れるから.

package slice_example

import "core:fmt"
import "core:math/rand"

Foo :: struct {
	x: int,
}

append_foo :: proc(foos: ^[dynamic]Foo) {
	random_age := rand.int_max(12) + 2
	append(foos, Foo{x = random_age})
}

print_foos :: proc(foos: []Foo) {
	for foo, i in foos {
		fmt.printfln("Foo[%d].x = %d", i, foo.x)
	}
}

mutate_foos :: proc(foos: []Foo) {
	// `&` 付与でスライスの要素を変更可能
	for &foo in foos {
		foo.x = rand.int_max(99)
	}
}

main :: proc() {
	foos: [dynamic]Foo
	append_foo(&foos)
	append_foo(&foos)

	print_foos(foos[:])
	mutate_foos(foos[:])
	print_foos(foos[:])
}

スライスをDeepCopyしたいとき

import "core:slice"

numbers: [128]int
first_20 := numbers[:20]
first_20_clone := slice.clone(first_20) // clone する

スライスの動的確保

// delete しないとメモリリーク
int_slice := make([]int, 128)

スライスのリテラル

numbers := []int{1, 2, 3}
// これは以下の操作と等価
_nums := [3]int{1, 2, 3}
numbers := _nums[;]

スライスのリテラルの落とし穴

numbers: []int // グローバル変数

set_numbers :: proc() {
	// tmp := [3]int{7, 42, 13}; numbers = tmp[:] と同じ
	numbers = {7, 42, 13} // この {..} のリテラルは proc 内だけで有効なことに注意!
	fmt.println(numbers) // [7, 42, 13]
}

main :: proc() {
	set_numbers()
	fmt.println(numbers) // [4296937425, 1, 6170161600] 解放されたメモリの内容を参照しているので不定な値が出力される
}
zenwerkzenwerk

Map(連想配列)

Mapは delete しないとメモリリークするので注意

m: map[string]int // 宣言
m["foo"] = 1
m["bar"] = 2
m["baz"] = 3

// 要素の削除
delete_key(&m, "foo")

// 要素の取得
if v, ok := m["bar"]; ok {
	fmt.println(v)
}

// 実行時エラーにはならない
does_not_exist := m["foo"]
fmt.println(does_not_exist) // 0

// in, not_in
fmt.println("bar" in m) // true
fmt.println("foo" not_in m) // true

// for
for k, v in m { ... }
for k, &v in m { ... } // v を変更したいとき

// delete しないとメモリリーク
delete(m)

作成時のメモリ指定

アロケータの指定

m := make(map[string]int, context.foo_allocator)
// 初期容量の指定
m := make(map[string]int, 64, context.temp_allocator)

Mapを使って集合をつくる

Odinは集合(Set)をビルトインで持たないので、Mapを使って表現する.

set: map[string]struct{} // struct{} は匿名型を表す(無名の型)
set["foo"] = {} // 値の追加
if "foo" in set { ... }

カスタムイテレータ

package iter_example

import "core:fmt"

Foo :: struct {
	x:    int,
	used: bool,
}

// イテレーションの状態を保存する構造体
Foo_Iterator :: struct {
	index: int,
	data:  []Foo,
}

// イテレータの作成
make_foo_iterator :: proc(data: []Foo) -> Foo_Iterator {
	return {data = data}
}

// イテレーションの実行
foo_iterator :: proc(it: ^Foo_Iterator) -> (val: Foo, idx: int, cond: bool) {
	cond = it.index < len(it.data)

	for ; cond; cond = it.index < len(it.data) {
		// used が false の場合はスキップしループに現れない
		if !it.data[it.index].used {
			it.index += 1
			continue
		}

		val = it.data[it.index]
		idx = it.index
		it.index += 1
		break
	}

	return
}

main :: proc() {
	// 128 個の Foo を用意する
	foos := make([]Foo, 128)
	foos[10] = {
		x    = 7,
		used = true,
	}

	// used ==  true の場合のみ現れるイテレータ
	it := make_foo_iterator(foos[:])
	for val in foo_iterator(&it) {
		fmt.println(val) // foos[10] のみ出力される
	}
}
zenwerkzenwerk

文字列

コード中に動的に構築される文字列はアロケータによってメモリが動的にメモリが割り当てられるためメモリ管理が必要.
コード中にべた書きされた文字列リテラルは data 領域に置かれる. これを delete しようとするとクラッシュするので注意.

allocated_string := strings.clone("Hellope!", context.allocator)
// 解放する必要がある
delete(allocated_string)

文字列のイテレーション

for r in "Hello" {
    fmt.println(r) // r は rune 型
}

rune型

UTF-8のコードポイントを表す型.
コードポイントは 多くの場合 一文字を表す.
コードポイントは1~4バイトの範囲で、文字によって可変.

文字列のスライス

スライスはバイト単位で行うため、文字列に対して行うと文字化けすることが多い.

str := "小猫咪"
sub_str := str[1:]
fmt.println(sub_str) // ��猫咪

文字化けさせずに部分文字列を得たい場合は strings.substring_from を使う.

str := "小猫咪"
sub_str, _ := strings.substring_from(str, 1)
fmt.println(sub_str) // 猫咪

イテレーションforでルーンのバイト数を得る方法

str := "小猫咪"

// `i` にルーンのバイト数が入る
for r, i in str {
	fmt.println(i, r)
}

出力は以下

0 小
3 猫
6 咪

バイト数が分かれば、スライス操作でも部分文字列を得ることができる.

str := "小猫咪"

for r, i in str {
	from_start := str[:i + utf8.rune_size(r)]
	fmt.println(from_start)
}

出力は以下

小
小猫
小猫咪

書記素クラスタ

複数のコードポイントで一文字を表す文字

str := "g̈"
for r in str {
    fmt.println(r)
}
g
̈

書記素クラスタを扱いたい場合は以下をつかうこと. ただしやや重い関数とのこと.
https://pkg.odin-lang.org/core/unicode/utf8/#decode_grapheme_clusters

フォーマット文字列

動的に文字列を組み立てるのでメモリ確保処理が必要.
tprintfaprintf, bprintf を使う.
t → temp_allocator
a → アロケータを指定できる
b → スタック上のバッファを利用する

name := "taro"
age := 7

// temp_allocator
fmt.tprintf("%v is %v", name, age) // taro is 7

// アロケータ指定
fmt.aprintf("%v is %v", name, age, allocator = context.temp_allocator)

// スタック領域上のバッファを利用する
buf: [128]byte
fmt.bprintf(buf[:], "%v is %v", name, age)

ストリングビルダーによる文字列の構築

でかい文字列を扱う場合の筆者おすすめの方法とのこと.
...省略...

Windows の文字列

Windows API の文字列は UTF-16 かつヌル終端文字が必要.
Windows はこれをワイド文字列と呼び wstring などと表記される.
Odin は UTF-8 なので UTF-8 ↔ wstring の変換が必要になる.

import "core:sys/windows"

// UTF-8 を wstring へ
windows.utf8_to_wstring("foo bar baz")

// wstring を UTF-8 へ
title_wstr: [128]windows.WCHAR
title_len := windows.GetWindowTextW(hwnd, raw_data(&title_wstr), len(title_wstr)) // ウィンドウのタイトルを取得
title_str, err := windows.wstring_to_utf8(raw_data(&title_wstr), int(title_len))

if err == nil {
	fmt.println(title_str)
}
zenwerkzenwerk

暗黙の Context 型変数

Odin では関数呼び出し時、context構造体が暗黙的に渡される.
base/runtime/core.odin に以下の定義.
関数内で context に加えた変更は、関数内でのみ有効で呼び出し元には伝播しない.

Context :: struct {
	allocator:              Allocator, // デフォルトのメモリアロケータ
	temp_allocator:         Allocator, // デフォルトのテンポラリメモリアロケータ
	assertion_failure_proc: Assertion_Failure_Proc, // `assert(cond, "Error message")` が失敗したときに呼ばれる関数
	logger:	   	            Logger, // デフォルトロガー "core:log"
	random_generator:       Random_Generator, // 乱数生成器の定義

	// ユーザーが自由に利用できる拡張データへのポインタとindex
	user_ptr:   rawptr,
	user_index: int,

	// Internal use only
	_internal: rawptr,
}

context.allocator

context.allocator を変更すると、それ以降関数で使用されるデフォルトのメモリアロケータを上書きできる.
以下の場合におすすめ

  1. 他のアロケータを使いたいが、関数引数にアロケータを指定できるシグネチャがない
  2. スコープ内のすべてのコードで指定したアロケータを使いたいとき.
    • 例えば main() の先頭で上書きするとプログラム全体で上書きできる.

「1」のパターンを避けるため、動的にメモリを割り当てる関数は、アロケータを受け渡せるようにするのが良い

some_func :: proc(allocator := context.allocator) -> []int {
	ints := make([]int, 4096, allocator)
	...
	return ints
}

context.temp_allocator についてはデフォルトで用意されているもので問題ない. ここを変更したいケースはほぼないはず.

context.assertion_failure_proc

assert失敗時に実行される関数の設定.
NOTE: 戻り値の型 -> ! はプログラムがクラッシュし継続しないことを表す.

assert_fail :: proc(prefix, msg: string, loc := #caller_location) -> ! {
	fmt.printfln("assert failed. detail: %s at line: %v", msg, loc)
	runtime.trap() // OSに障害発生を伝える関数
}

main :: proc() {
	context.assertion_failure_proc = assert_fail
	assert(false, "FAILED!!!")
}

context.logger

import "core:log"
import "core:os"

main :: proc() {
	mode: int = 0
	when ODIN_OS == .Linux || ODIN_OS == .Darwin {
		mode = os.S_IRUSR | os.S_IWUSR | os.S_IRGRP | os.S_IROTH
	}

	logh, err := os.open("log.txt", (os.O_CREATE | os.O_TRUNC | os.O_RDWR), mode)

	if err == os.ERROR_NONE {
		os.stdout = logh // 標準出力の接続先を `log.txt` に上書きする(fmt.print 等もファイルに書き出される)
		os.stderr = logh // 同様
	}

	// 三項演算子
	logger := err == os.ERROR_NONE ? log.create_file_logger(logh) : log.create_console_logger()
	// logger を上書きしたので、これ以降のログ出力が変更される
	context.logger = logger

	if err == os.ERROR_NONE {
		log.destroy_file_logger(logger)
	} else {
		log.destroy_console_logger(logger)
	}
}

context.{user_ptr,user_index}

foo := Foo { ... }
context.user_ptr = &foo
// 関数の呼び出し先で
context_foo := (^Foo)(context.user_ptr)
// Foo を使った処理などする...