Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Go 1.26 Lets new() Take an Expression. Delete Your Pointer Helpers

2026年10月15日に「Dev.to」が公開したITニュース「Go 1.26 Lets new() Take an Expression. Delete Your Pointer Helpers」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

Go 1.26で`new()`関数が強化され、`new(値)`のように直接ポインタを作成できるようになった。これにより、これまで値のポインタ生成に必要だったカスタムヘルパー関数の多くが不要になる。`go fix`コマンドでコードを自動修正でき、記述が簡潔になる。古いGoバージョン対応など一部例外はある。

ITニュース解説

システムエンジニアを目指す皆さん、Go言語の最新情報に注目しているだろうか。Go言語はシンプルで効率的なプログラミング言語として、ウェブサービスやインフラ開発で広く使われている。そのGo言語がバージョン1.26で、プログラマの日常的な悩みを一つ解決する大きな変更を加えた。それが、組み込み関数 new() の進化だ。

まず、Go言語における「ポインタ」の役割を理解しよう。ポインタとは、メモリ上の特定のアドレス、つまりデータがどこに保存されているかを示す「住所」のようなものだ。変数そのものにアクセスするのではなく、その変数が格納されている場所を指し示すことで、プログラムはより柔軟な処理を行える。

なぜポインタが必要になるかというと、特定の場面で「値が存在しない」状態と「値が0である」状態を区別したい場合があるからだ。例えば、ユーザー設定を扱う場面で「テーマが設定されていない(nil)」のと「テーマが『デフォルト』に設定されている」のは全く違う意味を持つだろう。データベースやAPIとの連携において、特定のフィールドが「送信されなかった」のか、「値が0として送信された」のかは重要な違いとなることが多い。Go言語には他の多くの言語にあるような Optional[T] のような型がないため、この「存在しない」状態を表現するには、フィールドをポインタ型にするのが一般的な方法だった。ポインタが nil であれば「存在しない」、nil でなければ値が存在すると判断できる。

しかし、これまでのGo言語には一つ不便な点があった。構造体のような複雑な型に対しては、&S{a: 3} のように簡単にポインタを作成できたが、数値リテラルや関数呼び出しの結果のような単純な値からは直接ポインタを作成できなかったのだ。例えば、&42 や &time.Now() のように書くと、Goコンパイラはエラーを出し、プログラムを動かせなかった。これは、42 のような定数や time.Now() のような関数呼び出しの結果が一時的な値であり、メモリ上の固定されたアドレスを持たないためだった。

この課題に対し、Go言語の開発者たちは同じような「ポインタヘルパー関数」を何度も自作してきた。例えば、func ptr[T any](v T) *T { return &v } のような関数だ。このヘルパー関数は、引数として受け取った値 v を一旦一時的な変数に格納し、その変数のアドレスを返すことで、数値リテラルや関数呼び出しの結果に対してもポインタを取得できるようにしていた。proto.String や k8s.io/utils/ptr.To など、Goのエコシステムでは多くのライブラリが同様のヘルパー関数を提供していた。これにより、Retries: ptr(0) のように記述することで、「リトライ回数が0に設定されている」という意図を明確に表現できた。

Go 1.26では、この長年の不便さが解消された。組み込み関数 new() が、これまでの「型」だけでなく「式」を受け付けるようになったのだ。つまり、new(42) や new(time.Now()) のように直接記述できるようになった。これは、これまでのポインタヘルパー関数が内部で行っていた処理を new() 関数自体が行ってくれることを意味する。new(x) は、x の型の新しい変数をメモリ上に確保し、その変数に x の値をコピーして初期化し、その変数のポインタを返す。結果として、これまで自作していた ptr[T] や IntPtr といったヘルパー関数のほとんどが不要になる。

この変更は、特にJSONのエンコード/デコードやProtocol Buffersのようなシリアライゼーションパッケージを使う際に役立つ。これらのパッケージは、前述の「オプションの値」を表現するためにポインタを多用するからだ。

既存のコードベースに散らばるポインタヘルパーの呼び出しを一つ一つ手動で書き換えるのは大変な作業だ。しかし、Go 1.26には go fix -newexpr という便利なコマンドが用意されている。このツールは、既存のヘルパー関数(ptr[T] のような形)を自動的に検出し、その呼び出し箇所を新しい new(expression) 形式に書き換えてくれる。大規模なプロジェクトでも、このツールを使えば効率的にコードを現代化できる。まず go fix -diff -newexpr ./... で変更内容をプレビューし、問題なければ go fix -newexpr ./... を実行するとよい。

ただし、go fix がすべてのケースを自動的に書き換えてくれるわけではない。いくつかの例外がある。 まず、型変換が必要な場合だ。new(42) は *int 型のポインタを返す。もし、プログラムが *int64 型のポインタを期待している場合、new(42) はそのままでは使えない。この場合、new(int64(5000)) のように明示的に型変換を行う必要がある。go fix はこのような型が異なるケースでは変換をスキップするため、手動での修正が必要になる。

次に、Go 1.26より前のバージョンをサポートする必要があるライブラリの場合だ。new(expression) はGo 1.26以降の言語仕様なので、もしあなたのライブラリが古いGoバージョンとの互換性を保つ必要があるなら、一時的にヘルパー関数を残しておく必要がある。

また、new 関数自体を「関数値」として別の関数に渡すことはできない。例えば、スライス内の要素に関数を適用する mapSlice のような関数で mapSlice(names, new) と書くことはできない。このような場合は、mapSlice(names, ptr[string]) のように、引き続きヘルパー関数を利用するか、func(s string) *string { return new(s) } のようなクロージャを使用する必要がある。

さらに、new(x) と &x は似ているようで異なる点がある。new(x) は常に x の値のコピーを持つ新しい変数を確保し、そのポインタを返す。一方、&x は既存の変数 x のアドレスを直接取得する。つまり、new(x) で得られたポインタを介して値を変更しても、元の x には影響しないが、&x で得られたポインタを介して値を変更すると、元の x も変更される(エイリアシング)。もしエイリアシングを意図しないなら new(x) を、既存の変数を直接操作したいなら &x を使うべきだ。

この新機能は、ポインタの生成をより簡潔にし、コードの可読性を向上させる。しかし、その恩恵を最大限に受けるためには、go.mod ファイルのGoバージョンを1.26以降に更新し、go fix ツールを適切に利用することが重要だ。そして、go fix が変換しなかった箇所や、ヘルパー関数を残す方が適切と判断される箇所については、プログラマ自身が慎重にレビューし、必要に応じて手動で調整する必要がある。Go言語の進化は、開発者がより効率的で堅牢なソフトウェアを構築するための基盤を着実に強化していると言えるだろう。

関連コンテンツ

関連IT用語