Go 1.26 の new() 式化と errors.AsType を実際に動かす

Go 1.26.0 が 2026年2月10日にリリースされた。話題の中心は Green Tea GC がデフォルトになったことだが、日々のコードに一番効くのは言語仕様と標準ライブラリの地味な改善だ。この記事では、実際に手を動かして違いが分かる2つの機能を取り上げる。

  • new() 組み込み関数が「型」だけでなく「式」を受け取れるようになった
  • errors パッケージにジェネリックで型安全な errors.AsType が追加された

どちらも既存コードのボイラープレートを減らす変更で、既存の書き方も引き続き動く。

new() が式を取れるようになった意味

これまで new(T) は型 T を渡してゼロ値へのポインタ *T を得る関数だった。「特定の初期値を持つポインタ」が欲しいときは、いったん変数に入れてからそのアドレスを取る必要があった。

1
2
3
// Go 1.25 まで: 初期値付きポインタを作るには一時変数が要る
n := 42
p := &n

これは特に、JSON の省略可能フィールド(*int などのポインタ型)に値を入れるときに面倒だった。ヘルパー関数 func ptr[T any](v T) *T { return &v } を自作していた人も多いだろう。

Go 1.26 では new() に式を直接渡せる。式を評価し、その結果を格納した新しい変数へのポインタを返す。

1
2
3
// Go 1.26: 式をそのまま渡せる
p := new(42) // *int、指す先は 42
s := new([]int{1, 2}) // *[]int

errors.AsType でエラー抽出が型安全になる

従来の errors.As は、ポインタのポインタを第2引数に渡す形で、呼び出し側が対象の変数を用意する必要があった。

1
2
3
4
5
// 従来: 変数を用意して &target を渡す
var appErr *AppError
if errors.As(err, &appErr) {
// appErr を使う
}

Go 1.26 の errors.AsType はジェネリックで、抽出したい型を型引数で指定し、値と bool を返す。シグネチャは次のとおり。

1
func AsType[E error](err error) (E, bool)

事前の変数宣言が不要になり、対象が error を実装していなければコンパイル時に弾かれる。従来 errors.As で型が合わないと実行時 panic になっていたケースが、コンパイルエラーとして検出できる。

実際に試す

前提: Go 1.26.0(go versiongo1.26.0 以上であること)。macOS / Linux / Windows いずれでも同じ。まずバージョンを確認する。

1
2
go version
# 例: go version go1.26.0 darwin/arm64

作業ディレクトリを作ってモジュールを初期化する。

1
2
mkdir try-go126 && cd try-go126
go mod init example.com/try-go126

以下を main.go として保存する。2つの新機能を1ファイルで確認する。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import (
"errors"
"fmt"
)

// アプリ固有のエラー型
type AppError struct {
Code int
Message string
}

func (e *AppError) Error() string {
return fmt.Sprintf("app error %d: %s", e.Code, e.Message)
}

// 省略可能フィールドを持つ構造体(JSON では *int を nil で省略する用途を想定)
type Config struct {
Name string
Timeout *int // nil なら未設定
}

func main() {
// (1) new() が式を取れる: 初期値付きポインタを一時変数なしで作る
cfg := Config{
Name: "worker",
Timeout: new(30), // Go 1.25 までは書けなかった
}
fmt.Printf("Name=%s Timeout=%d\n", cfg.Name, *cfg.Timeout)

// (2) errors.AsType: 型引数で欲しいエラー型を指定し、値と ok を受け取る
err := fmt.Errorf("処理に失敗: %w", &AppError{Code: 503, Message: "service unavailable"})

if appErr, ok := errors.AsType[*AppError](err); ok {
fmt.Printf("code=%d message=%q\n", appErr.Code, appErr.Message)
} else {
fmt.Println("AppError ではありません")
}
}

実行する。

1
go run main.go

期待される出力。

1
2
Name=worker Timeout=30
code=503 message="service unavailable"

new(30) が一時変数なしでポインタを生成し、errors.AsType[*AppError]fmt.Errorf%w でラップされたエラーから *AppError を型安全に取り出せていることが確認できる。

従来の書き方との比較

同じ抽出を errors.As で書くと次のようになる。両方 Go 1.26 で動くので、差分を見比べられる。

1
2
3
4
5
// 従来スタイル(Go 1.26 でも動く)
var appErr *AppError
if errors.As(err, &appErr) {
fmt.Printf("code=%d message=%q\n", appErr.Code, appErr.Message)
}

errors.AsType 版は事前宣言の1行が消え、型がその場で明示されるぶん読みやすい。既存コードを無理に置き換える必要はないが、新規コードでは型引数版のほうが panic の余地が少なく安全だ。

まとめ

Go 1.26 の new() 式化と errors.AsType は、どちらも「これまで自作ヘルパーや定型句で埋めていた隙間」を言語・標準ライブラリ側が埋めてくれる変更だ。派手さはないが、ポインタ生成とエラー抽出という頻出パターンが1行短く、型安全になる。手元に Go 1.26.0 があれば、上のコードをそのまま貼って挙動を確かめてほしい。

出典