世界を動かす技術を、日本語で。

C3を学ぶ

2025年5月29日原文(alloc.dev)

概要

  • C3言語 の学習体験記録
  • C3の特徴 とCとの比較
  • 主要な構文 (Hello World、ループ、enum、deferなど)の紹介
  • インストールから簡単なプロジェクト作成 までの流れ
  • リアルタイムでの学習記録 による率直な感想と気付き

C3言語学習体験記

  • C3 は、既存の C言語 を基盤としつつ、より 高い生産性新機能 を提供するプログラミング言語
  • 低レベルシステム言語 に親しんできた筆者が、 新しい言語への好奇心 でC3を学習
  • リアルタイム執筆 のため、他記事では省略されがちな 細かな気付き課題点 を記録
  • 言語ごとに異なる パラダイムアイデア表現方法 の違いを体験
  • C3でどんな プロジェクト が向いているかを模索

C3とは

  • C3公式サイト によれば、C3は「Cの上にCで構築」し、 エルゴノミクス最適化新機能 を提供
  • 主な特徴
    • モジュールシステム
    • 演算子オーバーロード
    • ジェネリクス
    • コンパイル時実行
    • セマンティックマクロ
    • 組み込みビルドシステム
    • エラー処理
    • defer、値メソッド、enumの拡張
    • サブタイプ、コントラクト、スライス、foreach、動的型 など

言語の概要

  • C3リファレンス を見ながら、主要な機能や構文をピックアップ
  • C言語 に似ている部分も多いが、 新しい表現力使いやすさ が強化

Hello World

  • サンプルコード
    import std::io;
    fn void main() {
      io::printn("Hello, World!");
    }
    
  • C言語 に近いが、関数定義に fnキーワード を明示的に使用
  • モジュールのimport はサブモジュールまで再帰的にインポート
  • 名前衝突abc::Context のように 名前空間 で解決
  • print関数 は多くの型を自動表示、 デバッグ に便利
  • フォーマット指定子 はCとほぼ同じだが、enumも文字列化される

foreachループ

  • サンプル
    fn void example_foreach(float[] values) {
      foreach (index, value : values) {
        io::printfn("%d: %f", index, value);
      }
    }
    
  • foreach構文 を標準搭載、参照渡しも可能(&value
  • break/continue も期待通り動作
  • forより高レベル な意図表現、ロジックエラー減少

whileループ

  • サンプル
    int a = 10;
    while (a > 0) {
      a--;
    }
    while (Point* p = getPoint()) {
      // ...
    }
    
  • C99以降 のforループのように、while条件内で変数宣言が可能

enum型とswitch文

  • サンプル
    enum Height : uint { LOW, MEDIUM, HIGH, }
    fn void demo_enum(Height h) {
      switch (h) {
        case LOW:
        case MEDIUM:
          io::printn("Not high");
        case HIGH:
          io::printn("High");
      }
    }
    
  • 暗黙的なbreak が特徴、nextcaseで明示的フォールスルー
  • 全パターン網羅時はdefault不要、コンパイラが警告
  • Duff’s Device のようなジャンプテーブル的な使い方も可能
  • @jump属性で 最適化コンパイラ によるジャンプテーブル化を強制

deferキーワード

  • サンプル
    fn void test(int x) {
      defer io::printn();
      defer io::print("A");
      if (x == 1) return;
      {
        defer io::print("B");
        if (x == 0) return;
      }
      io::print("!");
    }
    
  • スコープ終了時 に逆順でdefer文が実行
  • リソース解放クリーンアップ に非常に便利
  • Cのgoto cleanup よりも直感的・安全
  • defer catchdefer tryエラー時の自動処理 も可能

struct型

  • サンプル
    struct MyData {
      char* name;
      Callback open;
      Callback close;
      Status status;
      struct other { int value; int status; }
      struct { int value; int status; }
      union { Person* person; Company* company; }
      union either { int this; bool or; char* that; }
    }
    
  • サブ構造体匿名/名前付きunion を柔軟に宣言
  • タグ付きユニオン もenumとanonymous unionで簡単実装
  • Cでも可能 だが、C3はより明示的・安全な表現

エラー処理

  • サンプル
    int? a = 1; // オプショナル型
    int? b = io::FILE_NOT_FOUND?;
    faultdef OOPS, LOTS_OF_OOPS, USER_ERROR;
    fn int? get_value();
    if (catch excuse = get_value()) // エラー時処理
    int foo = maybe_function()!; // エラーなら即リターン
    
  • オプショナル型?)と Excuse型 でエラーを明示管理
  • catch構文 でエラー検知とハンドリング
  • !演算子 で例外発生時の即リターン
  • 表現力の高いエラー処理、Cよりも安全で直感的

次のステップ:C3の環境構築と簡単なプロジェクト

  • C3のインストール 方法紹介
  • 新規プロジェクト作成 手順
  • C3で電卓アプリ を作成する流れ
    • 必要な機能の整理
    • ユーザー入力取得
    • トークナイザー作成
    • パーサー作成
  • C3を使った開発の実際的な流れ を体験

まとめ

  • C3言語 はCに親しんだ開発者にとって 移行しやすく、かつ 現代的な機能 を多数提供
  • リアルタイム学習 だからこその率直な感想や細かな気付き
  • 新しい言語パラダイム の発見と、それが 自分のプロジェクト にどう生きるかの模索
  • C3の特徴 を活かした開発事例や、 他言語との比較 も今後の課題

Hackerたちの意見

C3は期待できそうだけど、nullをサポートする言語にはnull制限型が必要だよね。あの契約コメントみたいなのじゃなくて。もし全てにnullチェックをしなきゃいけないなら、Javaを書けばいいし…実際、Javaもこれを修正しようとしてるみたいだよね。https://openjdk.org/jeps/8303099

こういう関数契約についてはちょっと迷ってる。別の言語で10年見てきたけど、実際には使ったことがないから、どう思うかは言えないな。でも、コメントの中にあるのはちょっと変だよね。

面白い問題だね。最初は``と&の構文を両方試してみたんだ。int&が参照(非null)で、intがポインタって感じ。そこで気づくのは2つのことだね。1. ほとんどのポインタパラメータは非nullにしたい。2. 非null変数をコンストラクタなしで言語に組み込むのはすごく難しい。ZIIのようにコンストラクタやデストラクタを避けるアプローチは、参照値ともうまくいかないし。結局、値が準有効な状態になる期間ができちゃうんだ。非null型は割り当てが必要で、最初に割り当てられる前は壊れた状態だからね。C3で「型安全」な非null型を作ることは確かに可能だけど、それは言語に組み込まれてはいないんだ。

Nimは、2つの明示的で制限されたnullable型、ポインタと参照だけを持つことでこの問題を解決してる。ポインタは手動で管理され、参照は自動で管理される。どちらも最初はnilから始まり、参照されるオブジェクトは手動でインスタンス化しなきゃいけない。言語全体は、スタック値とスタック管理された隠れたユニークポインタを使った値渡しで構築されてる。CやC++ライブラリにインターフェースを作る以外では、refやポインタを使う必要はほとんどない。40,000行のプロダクションアプリを、参照やポインタ型なしで書いたことがある。必要なケースは、可変値として複合型や動的コンテナを渡すことでほぼカバーされる。ポインタや参照のセマンティクスを行うことは不可能だから、ライフタイムはすでに管理されてるし、セマンティクス的にはただの値なんだ。

なんで問題を解決する方法が一つしかないの?

C3に関する他の興味深いリンクもいくつかあるよ:インタビュー: - https://www.youtube.com/watch?v=UC8VDRJqXfc - https://www.youtube.com/watch?v=9rS8MVZH-vA いろんなタスクをC3でやってるシリーズ: - https://ebn.codeberg.page/programming/c3/c3-file-io/ プロジェクト: - ゲームボーイエミュレーター https://github.com/OdnetninI/Gameboy-Emulator/ - RISCV Bare metal Hello World: https://www.youtube.com/watch?v=0iAJxx6Ok4E - "Depths of Daemonheim"ローグライク https://github.com/TechnicalFowl/7DRL-2025 TsodingのC3の「初印象」ストリーム: - https://www.youtube.com/watch?v=Qzw1m7PweXs

C3とHareの両方を試した人いる?どうだった?二つの間にはかなりの重複があるみたいだね。[1] https://harelang.org/

Hareの問題は、(少なくとも最後にチェックしたときは)Linux/Unix専用で、そういう設計になってることだね。これだと多くの人にとっては致命的だよね。

Cの代替としてZigもあるよ。https://ziglang.org/

面白いことにC2もあるよ:http://c2lang.org

C4もあるけど、あれは爆発物かソフトウェアアーキテクチャをモデル化するための表記言語だよ。

そうそう、C3はC2のバリアントとして始まったんだよね。

Hacker Newsで議論の続きを見る