メモリ管理
NagiScript のメモリ管理戦略を学びます。
メモリ管理の設計方針
| 戦略 | 用途 | 特徴 |
|---|---|---|
| スタック確保 | 基本型、構造体 | 最速、自動管理 |
| Rc (参照カウント) | ヒープ確保 | 自動解放、安全 |
| Unsafe | ポインタ操作 | 開発者責任 |
NagiScript
には借用チェッカーはありません。メモリはスタック確保と参照カウント(Rc)による自動解放が基本です。ポインタ操作は
unsafe にまとめ、開発者の責任で扱います。
スタック確保
基本型と小さな構造体はスタックに確保されます:
val x = 42
val p = Point { x: 1.0, y: 2.0 }
fn add(a: i32, b: i32) -> i32 {
return a + b
}
Rc (参照カウント)
Rc.new(値) /
.get() はありますが、.get()
の戻り値が意図と異なる場合があります(要検証 /
pending)。大きなデータには現行で安定している「構造体 +
値渡し」を推奨します。
大きなオブジェクトや動的サイズのオブジェクトには Rc を使用する設計です:
val a = Rc.new(42)
val b = a // 参照カウント: 2
println(b.get())
参照カウントの動作
struct Data { x: i32 }
val a = Rc.new(Data { x: 10 }) // 参照カウント: 1
val b = a // 参照カウント: 2
val c = a // 参照カウント: 3
// b, c がスコープを抜けると a のみ残り、最終解放時に自動でメモリ解放
リスト (List)
List<T> は List.new() /
push / len / get /
set という API はありますが、LLVM コード生成で
zext i64 to i64
になりリンクに失敗する既知バグがあります。当面は使用を避けてください。
動的配列はヒープに確保される設計です。以下は現行ビルドではリンクに失敗するため、参考として記載します。
// 現行はリンク失敗バグがあるため使用不可
var numbers = List.new()
push(numbers, 10)
push(numbers, 20)
println(len(numbers))
推奨: 構造体 + 値渡し
現行で最も安定するのは、List や
Rc に頼らず、構造体 + 値渡しでデータを引き回す方法です。構造体はスタックに確保され、コピーも値で渡るため予期しない挙動がありません。
struct Data {
a: i32
b: i32
}
fn sum(d: Data) -> i32 {
return d.a + d.b
}
fn main() {
val d = Data { a: 10, b: 20 }
println(sum(d))
}
Unsafe ブロックとポインタ
ポインタは *T 型で表し、&x as *T
でアドレスを取得します。取り回しは
unsafe ブロックにまとめます:
fn read(ptr: *i32) -> i32 {
unsafe {
return *ptr
}
}
fn main() {
var x = 42
val p = &x as *i32
println(read(p))
}
ptr-> や
ptr.offset(n)
は未検証です。ポインタ演算は
*ptr と整数演算で代替してください。
C言語とのメモリ共有
C 標準関数は extern "C" fn で宣言して利用できます:
extern "C" fn malloc(size: i32) -> *u8
extern "C" fn free(ptr: *u8)
fn process_c_buffer() {
val ptr = malloc(100)
// ptr 経由でメモリを操作(unsafe 経由)
free(ptr)
}
