構造体と列挙型

独自の型を定義する方法を学びます。


構造体 (Struct)

定義

filename.ngs
struct Point {
    x: f64
    y: f64
}

struct Person {
    name: string
    age: i32
    email: string
}

インスタンス化

filename.ngs
val p = Point { x: 10.0, y: 20.0 }
val person = Person { name: "Alice", age: 30, email: "alice@example.com" }

フィールドアクセス

filename.ngs
val x = p.x
val name = person.name

// フィールドの変更(var のみ)
var p2 = Point { x: 1.0, y: 2.0 }
p2.x = 10.0  // OK: p2 は var

メソッド

impl ブロックで定義する

現行仕様(v0.2.0)

メソッドは impl 型名 { ... } ブロック内に fn として定義し、第一引数に self: 型名 の注釈を必須とします。戻り値は -> 構文と明示的な return で返します。整数・浮動小数の文字列化(str(x))や文字列 + 連結は現行では使えないため、math モジュールに依存する例は避けてください。

filename.ngs
struct Point {
    x: f64
    y: f64
}

impl Point {
    fn describe(self: Point) -> string {
        return "point"
    }
}

fn main() {
    val p = Point { x: 3.0, y: 4.0 }
    println(p.describe())  // point
}

列挙型 (Enum)

基本的な列挙型

現行仕様(v0.2.0)

enum の定義は裸のバリアント名 Red / Green を列挙します。バリアント値を生成するときは Color.Red のように括弧なしで生成できます(データを持つバリアントのみ括弧が必要、例: Some(42))。

filename.ngs
enum Color {
    Red
    Green
    Blue
}

val c = Color.Red

データを持つ列挙型

filename.ngs
enum Shape {
    Circle(f64)                    // radius
    Rect(f64, f64)                 // width, height
}

fn kind(s: Shape) -> string {
    val label = match s {
        Circle(r) => "circle",
        Rect(w, h) => "rect",
    }
    return label
}

fn main() {
    val c = Shape.Circle(5.0)
    val r = Shape.Rect(2.0, 3.0)
    println(kind(c))  // circle
    println(kind(r))  // rect
}
現行仕様(v0.2.0)

match のパターンは裸のバリアント名(Circle(r) など)と _ を使います。データ生成は Shape.Circle(5.0) のように () 付きです。math.PI / math.sqrt のようなモジュールは現行では存在しないため、上記はモジュールに依存しない例にしています。


Result 相当の enum(自作)

エラーハンドリングのための Result 型は現行では組み込みとして存在しません。enum で自作します。

filename.ngs
enum MathResult {
    Value(f64)
    DivByZero
}

fn divide(a: f64, b: f64) -> MathResult {
    if b == 0.0 {
        return MathResult.DivByZero()
    }
    return MathResult.Value(a / b)
}

fn main() {
    val result = divide(10.0, 3.0)
    val st = match result {
        Value(v) => "ok",
        DivByZero => "error",
    }
    println(st)
}

? 演算子

ロードマップ

エラーを上位関数に伝播させる ? 演算子は未実装です。引数に結果を返す enum を渡し、match で分岐する形で実装します。ジェネリクス(Result<T, E> のような Vec2<T>)は現行では未実装/未検証なため、上記は単一の型を使って自作した例です。


ジェネリクス(ロードマップ)

ロードマップ

Vec2<T> のようなジェネリクス構造体や fn first<T>(...) -> T のようなジェネリック関数は未実装/未検証です。struct Vec2 { x: f32, y: f32 } のように具体的な型で定義してください。型パラメータは List<i32> など一部で限定的にしか使えず、List<T> 自体も LLVM コード生成でリンクに失敗する既知バグがあるため、当面は構造体 + 値渡しを推奨します。

Option 相当の enum(自作)

現行仕様(v0.2.0)

Option 型も組み込みでは存在しないため enum で自作します。データを持つバリアント(Found(User))はパターン Found(u) で取り出せます。find_user は失敗時に Lookup.NotFound(括弧なし)で戻します。

filename.ngs
struct User {
    name: string
}

enum Lookup {
    Found(User)
    NotFound
}

fn find_user(id: i32) -> Lookup {
    if id == 1 {
        return Lookup.Found(User { name: "Alice" })
    }
    return Lookup.NotFound
}

fn main() {
    val user = find_user(1)
    match user {
        Found(u) => println(u.name),
        NotFound => println("not found"),
    }
}