go1.26 errors.As→errors.AsType で型安全になるとはどういうこと?

テック
Go

2026年2月15日

go 1.26.0 ではerrors.AsTypeがリリースされた。

これは、errors.Asに対してジェネリクスを活用して型パラメータを指定できるというもの。わかりやすいメリットとして、事前変数の定義が不要になることでコードがスッキリすることが挙げられる。

go
// Before
var pathErr *os.PathError
if errors.As(err, &pathErr) {
	...
}

// After
if pathErr := errors.AsType[*os.PathError](err); pathErr != nil {
	...
}

他の解説記事では、さらなるメリットとして「より型安全になる」と説明されていた。これだけではよくわからなかったので調べてみた。

結論としては、引数としてのエラー型を間違って渡した際に、

  • errors.As→ 実行時エラー
  • errors.AsType→ コンパイルエラー

になるという違いだった。

errors.Asは型をanyとして受け取った後、リフレクションで実行時に型を検証しており、 不正な型の場合はpanicする。これはコンパイル時には検出できない。

102func As(err error, target any) bool {
103	if err == nil {
104		return false
105	}
106	if target == nil {
107		panic("errors: target cannot be nil")
108	}
109	val := reflectlite.ValueOf(target)
110	typ := val.Type()
111	if typ.Kind() != reflectlite.Ptr || val.IsNil() {
112		panic("errors: target must be a non-nil pointer")
113	}
114	targetType := typ.Elem()
115	if targetType.Kind() != reflectlite.Interface && !targetType.Implements(errorType) {
116		panic("errors: *target must be interface or implement error")
117	}
118	return as(err, target, val, targetType)
119}

一方でerrors.AsTypeはジェネリクスを導入しているために、コンパイル時に型チェックが行われる

167func AsType[E error](err error) (E, bool) {
168	if err == nil {
169		var zero E
170		return zero, false
171	}
172	var pe *E // lazily initialized
173	return asType(err, &pe)
174}

以下のコードのように間違った型を渡してしまった際、errors.Asでは実行時までわからなかったものが、errors.AsTypeではコンパイル時に指摘してくれる。この違いにより「型安全になった」と言える。

go
package main

import (
	"errors"
	"os"
)

func main() {
	err := &os.PathError{Op: "open", Path: "/tmp/test"}

	// errors.As: 実行時パニック
	var wrongType string
	errors.As(err, &wrongType) // コンパイルOK、実行時パニック

	// errors.AsType: (コメントアウトを外すと)コンパイルエラー
	// wrongType, ok := errors.AsType[string](err)
	// _ = ok
}

https://go.dev/play/p/Ke5NNVqpuO-

さらに、reflectionを利用しなくなったことでパフォーマンス向上が見込めるということで、今後はerrors.AsTypeに乗り換えていくのが良さそう。