Go1.25 New Features
Go1.25が8月13日にリリースされ、リリースノートやブログが公開されています。この記事では前回のGo1.24 New Features に引き続き、Go1.25の新機能の中から気になった機能を紹介していきます。
spec
Go1.25では既存のGoのコードに影響を与える言語仕様の変更はありません。ただし、core typeの概念が削除され、専用の文章に置き換えられました。
詳細については、公式ブログをご参照ください。
tools
go command
go build -asanオプションは、プログラム終了時にデフォルトでメモリリークを検出するようになりました。
これは、Cによって割り当てられたメモリが解放されず、CまたはGoによって割り当てられた他のメモリから参照されていない場合にエラーを出します。
このエラーは、ASAN_OPTIONS=detect_leaks=0を設定することで無効にできます。
事前にビルドされたツールが一部リリースに含まれなくなります。コンパイラやリンカなどのコアツールチェーンのバイナリは引き続き含まれますが、ビルドやテスト、フォーマットで頻繁に呼び出されないツールは含まれません。
削除対象となるツールはaddr2line、buildid、nm、objdump、pprof、test2json、traceなどです。これらのツールのソースコードは引き続きGOROOTに含まれるため、初回実行時に自動的にビルドされ、キャッシュされます。既存のワークフローに影響を与えることなく、配布パッケージのサイズを大幅に縮小することが可能になります。
参考としてgo1.x.linux-amd64.tar.gzのサイズは1.24.0が75MBなのに対し、1.25.0が57MBと削減されています。
新しいgo.modのignoreディレクティブを使用して、goコマンドが無視するディレクトリを指定できます。指定したディレクトリとそのサブディレクトリ内のファイルは、allや./...などのパターンにマッチするとき無視されますが、モジュールのzipファイルには引き続き含まれます。
この機能は、多言語プロジェクトにおいて特に有用です。例えば、node_modulesやBazelが生成するbazel-*ディレクトリなど、大量のファイルを含むディレクトリがある場合、go build ./...やgo test ./...の実行時間を大幅に短縮できます。また、goplsのCPU使用率も改善されます。
使用例
// go.mod
module example.com/myproject
go 1.25
ignore node_modules
ignore bazel-*
ignore .build
新しいgo doc -httpオプションは、要求されたオブジェクトのドキュメントを表示するドキュメントサーバーを起動し、ブラウザでドキュメントを表示します。
新しいgo version -m -jsonオプションは、指定されたGoのバイナリファイルに埋め込まれたビルド情報(runtime/debug.BuildInfo)をJSON形式で出力します。
type BuildInfo struct {
    GoVersion string        // Goのバージョン(例: "go1.25.0")
    Path      string        // メインモジュールのパス
    Main      Module        // メインモジュールの情報
    Deps      []*Module     // 依存関係のスライス
    Settings  []BuildSetting // ビルド設定(CGOの状態、アーキテクチャなど)
}
goコマンドは、<meta name="go-import" content="root-path vcs repo-url subdir">構文を使用してモジュールパスを解決する際に、リポジトリのサブディレクトリをモジュールルートのパスとして使用できるようになりました。これにより、root-pathがバージョン管理システムvcsを持つrepo-urlのsubdirに対応することを示します。
特に単一のリポジトリ内で複数のGoモジュールを管理し、それぞれに異なるカスタムインポートパスを割り当てることが可能になるため、マイクロサービスアーキテクチャやモノレポ構成において有用です。
meta go-importの設定方法
meta go-importタグは、カスタムインポートパスを設定するためのHTMLのmetaタグで、Webサーバーのレスポンスの<head>セクションに記載します。
<!DOCTYPE html>
<html>
<head>
    <meta name="go-import" content="example.com/mymodule git https://github.com/user/mymodule">
</head>
<body>
    <p>Go to <a href="https://github.com/user/mymodule">GitHub repository</a></p>
</body>
</html>
動作
- ユーザーが
go get example.com/mymoduleを実行 - Goコマンドが
https://example.com/mymodule?go-get=1にHTTPリクエストを送信 - サーバーがmetaタグを含むHTMLを返却
 - Goコマンドがmetaタグの情報を使って実際のリポジトリからコードを取得
 
新しいworkパッケージパターンは、work(以前はmainと呼ばれていた)モジュール内のすべてのパッケージにマッチします。
使用例
# workモジュール内のすべてのパッケージをビルド
go build work
# workモジュール内のすべてのパッケージをテスト
go test work
# workモジュール内のすべてのパッケージをベット
go vet work
モジュールモードとワークスペースモードでの挙動の違い
- 
モジュールモード
- 
workは現在のモジュール内のすべてのパッケージを対象とする - 
go.modファイルで定義されたメインモジュールのパッケージのみ 
 - 
 - 
ワークスペースモード
- 
workはワークスペース内のすべてのモジュールのパッケージを対象とする - 
go.workファイルで定義された複数モジュールのパッケージすべて 
 - 
 
goコマンドがgo.modまたはgo.workファイルのgoディレクティブを更新する際、コマンドの現在のバージョンを指定するtoolchainディレクティブを追加しなくなりました。
vet
go vetコマンドに新しいアナライザが含まれます。
waitgroup: sync.WaitGroup.Addを不適切に使用している場合に検出されます。
問題のあるコード
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
    go func() {
        wg.Add(1)        // NG: goroutine内でAdd()
        defer wg.Done()
        // 処理
    }()
}
wg.Wait() // すべてのgoroutineが開始される前に完了する可能性
正しいコード
var wg sync.WaitGroup
for i := 0; i < 5; i++ {
    wg.Add(1)           // OK: goroutine開始前にAdd()
    go func() {
        defer wg.Done()
        // 処理
    }()
}
wg.Wait() // 確実にすべてのgoroutineを待機
hostport: fmt.Sprintf("%s:%d", host, port)の形式でnet.Dial用アドレスを構築している場合に、net.JoinHostPortを代わりに提案します。これは前者がIPv6アドレスを扱う際に、省略記法により意図しない表記をしてしまう可能性があるためです。
// 問題のあるコード
addr := fmt.Sprintf("%s:%d", host, port)
// 正しいコード
addr := net.JoinHostPort(host, strconv.Itoa(port))
runtime
GOMAXPROCSのデフォルト動作が変更されました。Goの以前のバージョンでは、GOMAXPROCSは起動時に利用可能な論理CPU数(runtime.NumCPU)がデフォルトで設定されていました。Go1.25では2つの変更点が追加されています。
- 
Linuxでは、ランタイムはプロセスを含むcgroupのCPU帯域幅制限を考慮します。CPU帯域幅制限によるCPU数が論理CPU数より低い場合、
GOMAXPROCSはより低い値に設定されます。Kubernetesなどのコンテナランタイムシステムでは、cgroupのCPU帯域幅制限は一般的に「CPU limit」オプションに対応します。「CPU requests」オプションは考慮しません。 - 
すべてのOSで、論理CPU数やcgroupのCPU帯域幅制限が変更された場合、ランタイムは定期的に
GOMAXPROCSを更新します。 
これらの動作は両方とも、GOMAXPROCS環境変数またはruntime.GOMAXPROCSによって手動でGOMAXPROCSが設定されている場合、自動的に無効になります。GODEBUGの設定 containermaxprocs=0とupdatemaxprocs=0でそれぞれ明示的に無効にすることもできます。
更新されたcgroup制限の読み取りをサポートするため、ランタイムはプロセスの存続期間中、cgroupファイル用にキャッシュされたファイルディスクリプタを保持します。
詳しくは公式ブログをご参照ください。
既存のガベージコレクタの一部を置き換える新しいガベージコレクタが実験的に利用できるようになりました。このガベージコレクタの設計は、より良い局所性とCPUスケーラビリティを通じて小さなオブジェクトのマーキングとスキャンの性能を向上させます。ベンチマーク結果は様々ですが、ガベージコレクションを頻繁に実行する実際的なプログラムでは、ガベージコレクションのオーバーヘッドが10〜40%程度削減される見込みです。
新しいガベージコレクタは、ビルド時にGOEXPERIMENT=greenteagcを設定して有効化できます。
ちなみに新しいガベージコレクタはGreen Teaと呼ばれていて、命名の由来はメインで実装したAustin Clements氏が、日本滞在中に毎日抹茶を飲みながらアルゴリズム開発の進捗を多く生めたからだそうです。
上記のGo Timeやプロポーザルでも言及されていますが、SIMDの活用などまだまだ改善する余地があるとのことで、今後さらにパフォーマンスの改善が期待できそうです。
ランタイムの実行トレースは長い間、アプリケーションの低レベル動作を理解し、デバッグするための強力で負荷の高い手段を提供してきました。しかしながら、実行トレースを継続的にファイルに書き込むコストとそのサイズのため、一般的に稀にしか発生しないイベントのデバッグでは実用的ではありませんでした。
新しいruntime/trace.FlightRecorderは、トレースをメモリ内のリングバッファに継続的に記録することで、ランタイムの実行トレースをキャプチャする軽量な手段を提供します。重要なイベントが発生した際、プログラムはFlightRecorder.WriteToを呼び出し、過去数秒間のトレースをファイルに書き込むことができます。
FlightRecorderによってキャプチャされる時間の長さとデータ量は、FlightRecorderConfig内で設定できます。
使用例
// フライトレコーダーの設定と開始
recorder := trace.NewFlightRecorder(
    trace.FlightRecorderConfig{
        MinAge: 5 * time.Second, // 過去5秒間のトレースを保持
        MaxBytes: 0,             // 0の場合サイズ上限なしで保持
    },
)
recorder.Start()
defer recorder.Stop()
// 問題検出時にトレースを保存
file, _ := os.Create("trace.out")
recorder.WriteTo(file)
panicをrecover後に再びpanicが発生し、recoverしないままプログラムが終了する場合に出力されるメッセージは、出力する値が同じであれば繰り返し表示されなくなりました。
Go1.25以前は以下のように出力していました。
    panic: PANIC [recovered]
      panic: PANIC
Go1.25からは以下のようにされます。
    panic: PANIC [recovered, repanicked]
出力する値が異なれば従来通りのメッセージが出力されます。
    panic: PANIC1 [recovered]
      panic: PANIC2
匿名仮想メモリ領域(VMA: Virtual Memory Area)名のカーネルサポート(CONFIG_ANON_VMA_NAME)があるLinuxでは、Goランタイムは匿名メモリマッピングにその目的に関する情報を付加するようになります。例えば、ヒープメモリに対して[anon: Go: heap]のような具合です。これはGODEBUGの設定 decoratemappings=0で無効にできます。
匿名VMAとは
- 匿名メモリ: 特定のファイルに結び付いていないメモリ領域(プログラムが動的に割り当てるメモリ)
 - VMA: Linuxカーネルがメモリ領域を管理する単位
 
before/after
- 
before: 
/proc/[pid]/mapsで表示される際、用途が不明で「[anon]」とのみ表示 - 
after: 「
[anon: Go: heap]」「[anon: Go: stack]」などのコンテキストに即した情報を付与 
compiler
Go1.25で、Go 1.21で混入したバグが修正されました。このバグはnilポインタチェックを不正に遅延させる可能性がありました。以下のように(不正に)正常に実行されていたプログラムは、今後(正しく)nilポインタ例外でパニックします。
package main
import "os"
func main() {
	f, err := os.Open("nonExistentFile")
	name := f.Name()
	if err != nil {
		return
	}
	println(name)
}
このプログラムは、エラーをチェックする前にos.Openの結果を使用しているため正しくありません。errがnon-nilの場合fの結果はnilであり、その場合f.Name()は内部でf.nameとnilのフィールドを参照しているのでパニックすべきです。しかし、Go 1.21から1.24では、コンパイラはnilチェックをエラーチェック後まで不正に遅延させ、プログラムが正常に実行されていました。これはGoの仕様に違反しています。Go1.25では正常に実行されません。(ちなみにf.Name()が呼ばれた時点でpanicはしないのは仕様通りです)
※リリースノートのサンプルコードだと分かりずらいので補足
以下のコードをGo1.25以前で実行した場合、panicにならずに③を出力してプログラムが正常終了します。そしてGo1.25以前のバージョンのまま②or④のコメントアウトを外して実行すると、①でpanicとなります。Go1.25以降で実行した場合は②or④のコメントアウトを外さずとも①でpanicするようになります。
package main
import (
	"fmt"
)
func main() {
	m := map[string]*struct {
		field int
	}{}
	t, ok := m[""]
	valid := t.field > 0 // ①
	// fmt.Printf("got: %v\n", t) // ②
	if !ok || !valid {
		fmt.Printf("got: %v\n", t) // ③
	}
	// fmt.Printf("valid: %v\n", valid) // ④
}
Go1.25のコンパイラとリンカは、DWARFバージョン5を使用してデバッグ情報を生成するようになりました。新しいDWARFバージョンは、Goバイナリのデバッグ情報に必要なメモリを削減し、特に大きなGoバイナリに対してリンク時間を短縮します。
デバッガへの影響
- 起動の高速化: デバッグ情報のサイズ削減により、起動時間が短縮される可能性
 - メモリ使用量の削減: デバッガがロードするデバッグ情報が少なくなるため、メモリ効率が向上
 
コンパイラは、より多くの状況でスライスのバッキングストア(スライスを構成するarray, len, capといった情報)をスタックに割り当てることができるようになり、性能が向上しました。この変更は、不適切にunsafe.Pointerを使用した場合、影響する可能性があります。例としてissue73199を参照してください。これらの問題を追跡するために、bisectツールを-compile=variablemakeフラグと組み合わせて使用して、問題の原因となる割り当てを見つけることができます。このような新しいスタック割り当ては-gcflags=all=-d=variablemakehash=nを使用してオフにすることもできます。
linker
リンカは-funcalign=Nコマンドラインオプションを受け入れるようになりました。これは関数エントリのアライメントを指定します。
デフォルト値はプラットフォーム依存であり、このリリースでは変更されていません。
new packages
testing/synctest
testing/synctestパッケージは、並行処理を行うコードのテストをサポートします。
Test関数は、隔離されたスコープである「バブル」内でテスト関数を実行します。バブル内では、時間は仮想化されます。timeパッケージの処理は疑似クロックで動作し、バブル内のすべてのgoroutineがブロックされている場合、クロックは瞬時に進みます。
Wait関数は、現在のバブル内のすべてのgoroutineがブロックされるまで待ちます。
encoding/json/v2
Go1.25では、ビルド時に環境変数GOEXPERIMENT=jsonv2を有効にすることで、JSONの新しい実装を使用できます。
有効にした場合、2つの新しいパッケージが利用可能になります。
- 
encoding/json/v2パッケージは、encoding/jsonパッケージの代替です- より良い性能: Marshal/Unmarshal処理の大幅な高速化
 - メモリ効率: ガベージコレクションの負荷を軽減する設計
 - ストリーミングサポート: 大きなJSONデータのストリーム処理
 - 型安全性の向上: より厳密な型チェックとエラーハンドリング
 - カスタマイゼーション: フィールドの命名規則やフォーマットの細かい制御
 - 互換性の改善: 様々なJSONバリアントとの相互運用性
 - エラー情報の詳細化: デバッグしやすいエラーメッセージ
 
 - 
encoding/json/jsontextパッケージは、JSON構文の低レベル処理を提供します- ストリーミングJSON読み取り・書き込みAPIを提供
 - トークンレベルでのJSON解析と生成が可能
 - メモリ効率的なJSON処理を実現
 - カスタムJSONフォーマットの実装をサポート
 - 既存の
encoding/jsonパッケージの基盤として機能 
 
さらに、jsonv2が有効な場合、
- 
encoding/jsonパッケージは内部でencoding/json/v2を使用します。MarshallingとUnmarshallingの動作は影響を受けませんが、パッケージ関数によって返されるエラーのテキストが変更される可能性があります - 
encoding/json/v2パッケージには、MarshallerとUnmarshallerを実装する際に使用できる多くのオプションやstruct tagが追加されます 
v2では多くのシナリオでv1よりも大幅に優れた性能を発揮します。基本的に、Marshalの性能は同等であり、Unmarshalはv2で大幅な高速化を見込めます。より詳細な分析については、github.com/go-json-experiment/jsonbenchリポジトリを参照してください。
詳細についてはプロポーザルやパッケージのコメントをご参照ください。
v1からv2へのマイグレーションについてはMigrating to v2を参照してください。
minor changes to packages
archive/tar
Writer.AddFSメソッドは、io/fs.ReadLinkFSを実装するfs.FSでシンボリックリンクをサポートするようになりました。
encoding/asn1
UnmarshalとUnmarshalWithParamsは、ASN.1タイプのT61StringとBMPStringをより一貫して解析するようになりました。これにより、以前受け入れられていた一部の不正な形式のエンコードが拒否される可能性があります。
crypto
MessageSignerは、署名するメッセージを自分でハッシュ化できる新しい署名インターフェースです。新しい関数SignMessageも導入され、SignerインターフェースをMessageSignerにアップグレードしようと試み、成功した場合はMessageSigner.SignMessageメソッドを使用し、そうでなければSigner.Signを使用します。これは、SignerとMessageSignerの両方をサポートしたいコードで使用できます。
プログラム開始後にGODEBUG設定のfips140を変更しても何も起きなくなりました。以前は許可されていないと文書化されており、変更された場合にパニックを引き起こす可能性がありました。
SHA-1、SHA-256、SHA-512は、AVX2命令が利用できない場合、amd64でより遅くなりました。2015年以降に製造されたほとんどすべてのプロセッサはAVX2をサポートしています。
crypto/ecdsa
新しいParseRawPrivateKey、ParseUncompressedPublicKey、PrivateKey.Bytes、PublicKey.Bytes関数とメソッドは、低レベルのエンコーディングを実装し、crypto/ellipticやmath/big関数とメソッドを駆使しなくてよくなりました。
従来の実装との比較
従来、ECDSAキーの生のバイト形式との変換には、楕円曲線の数学的操作や大きな整数の処理が必要でした。
// Go1.24以前の方法 - 複雑で間違いやすい
func privateKeyToBytes(priv *ecdsa.PrivateKey) []byte {
    // math/bigを使用した手動でのバイト変換
    return priv.D.Bytes()
}
func publicKeyToBytes(pub *ecdsa.PublicKey) []byte {
    // crypto/ellipticの関数を使用した座標の手動結合
    x := pub.X.Bytes()
    y := pub.Y.Bytes()
    // 非圧縮形式のエンコーディングを手動で構築
    uncompressed := make([]byte, 1+len(x)+len(y))
    uncompressed[0] = 0x04 // 非圧縮フラグ
    copy(uncompressed[1:], x)
    copy(uncompressed[1+len(x):], y)
    return uncompressed
}
// Go1.25の新しい方法 - 簡潔で安全
func privateKeyToBytes(priv *ecdsa.PrivateKey) []byte {
    return priv.Bytes() // 直接的なメソッド呼び出し
}
func publicKeyToBytes(pub *ecdsa.PublicKey) []byte {
    return pub.Bytes() // 自動的な非圧縮形式エンコーディング
}
// 逆変換も同様に簡潔
func bytesToKeys(privBytes, pubBytes []byte) (*ecdsa.PrivateKey, *ecdsa.PublicKey, error) {
    priv, err := ecdsa.ParseRawPrivateKey(privBytes)
    if err != nil {
        return nil, nil, err
    }
    
    pub, err := ecdsa.ParseUncompressedPublicKey(pubBytes)
    if err != nil {
        return nil, nil, err
    }
    
    return priv, pub, nil
}
この変更により、開発者は楕円曲線暗号の数学的詳細を理解することなく、ECDSAキーの生のバイト表現を安全かつ効率的に処理できるようになります。
FIPS 140-3モードが有効な場合、署名は4倍高速になり、非FIPSモードの性能と一致します。
crypto/ed25519
FIPS 140-3モードが有効な場合、署名は4倍高速になり、非FIPSモードの性能と一致します。
crypto/elliptic
Curveインターフェースの実装の一部で文書化されていないInverseとCombinedMultメソッドが実装されていましたが削除されました。
crypto/rsa
PublicKeyは、modulus値が秘匿されているとは言えなくなりました。VerifyPKCS1v15とVerifyPSSは、すべての入力が公開されリークする可能性があることをすでに警告しており、他の公開値からmodulusを復元できる数学的攻撃が存在します。
鍵生成は3倍高速になりました。
crypto/sha1
SHA-NI命令が利用可能な場合、amd64でハッシュ化が2倍高速になりました。
crypto/sha3
新しいSHA3.Cloneメソッドはhash.Clonerを実装します。
Apple M系プロセッサでハッシュ化が2倍高速になりました。
crypto/tls
新しいConnectionState.CurveIDフィールドは、接続を確立するために使用された鍵交換メカニズムを公開します。
新しいConfig.GetEncryptedClientHelloKeysコールバックは、クライアントがEncrypted Client Hello拡張を送信する際にサーバーが使用するEncryptedClientHelloKeyを設定するために使用できます。
RFC 9155に従い、SHA-1署名アルゴリズムはTLS 1.2ハンドシェイクで許可されなくなりました。
これらはGODEBUGの設定のtlssha1=1で再び有効化できます。
FIPS 140-3モードが有効な場合、TLS 1.2でExtended Master Secretが必要になり、Ed25519とX25519MLKEM768が許可されるようになりました。
TLSサーバーは、クライアントの最も優先されるプロトコルバージョンでなくても、最高のサポートされるプロトコルバージョンを優先するようになりました。
TLSクライアントとサーバーの両方が、仕様に従うことと仕様外の動作を拒否することにおいて、より厳格になりました。準拠するピアとの接続は影響を受けないはずです。
crypto/x509
CreateCertificate、CreateCertificateRequest、CreateRevocationListは、crypto.Signerだけでなくcrypto.MessageSigner署名インターフェースも受け入れることができるようになりました。これにより、これらの関数は、呼び出し元によってではなく署名操作の一部としてハッシュが行われる「ワンショット」署名インターフェースを実装する型を使用することができます。
CreateCertificateは、SubjectKeyIdが不足している場合、切り詰められたSHA-256を使用してそれを設定するようになりました。
GODEBUGの設定のx509sha256skid=0でSHA-1に戻します。
ParseCertificateは、負のpathLenConstraintを含むBasicConstraints拡張を含む証明書を拒否するようになりました。
ParseCertificateは、ASN.1 T61StringとBMPString型でエンコードされた文字列をより一貫して処理するようになりました。これにより、以前受け入れられていた一部の不正な形式のエンコーディングが拒否される可能性があります。
debug/elf
debug/elfパッケージはRISC-V ELF解析用の2つの新しい定数を追加します。
go/ast
FilterPackage、PackageExports、MergePackageFiles関数、およびMergeMode型とその定数は、長期間非推奨のObjectとPackage機構でのみ使用されているため、すべて非推奨となります。
新しいPreorderStack関数は、Inspectと同様に構文木を巡回し、サブツリーへの降下を制御しますが、便宜上、ノードの各ポイント以下のスタックも提供します。
go/parser
ParseDir関数は非推奨になります。
go/token
新しいFileSet.AddExistingFilesメソッドは、既存のFileをFileSetに追加することを可能にし、または任意のFileセットに対してFileSetを構築することを可能にし、長時間実行されるアプリケーションで単一のグローバルFileSetに関連する問題を軽減します。
go/types
Varは、変数を以下のいずれかとして分類するVar.Kindメソッドを持つようになりました:パッケージレベル変数、レシーバ変数、引数、戻り値、ローカル変数、または構造体フィールド。
新しいLookupSelection関数は、既存のLookupFieldOrMethod関数と同様に、指定された名前とレシーバ型のフィールドまたはメソッドを検索しますが、結果をSelectionの形式で返します。
hash
新しいXOFインターフェースは、SHAKEなどの任意または無制限の出力長を持つハッシュ関数である「拡張可能出力関数」によって実装できます。
新しいClonerインターフェースを実装するハッシュは、その状態のコピーを返すことができます。すべてのHashの実装は現在Clonerを実装しています。
hash/maphash
新しいHash.Cloneメソッドはhash.Clonerを実装します。
io/fs
新しいReadLinkFSインターフェースは、ファイルシステム内のシンボリックリンクを読み取る能力を提供します。
log/slog
GroupAttrsはAttr値のスライスからグループAttrを作成します。
Recordは、ソース位置を返すか、利用できない場合はnilを返すSourceメソッドを持つようになりました。
mime/multipart
新しいヘルパー関数FileContentDispositionは、マルチパートContent-Dispositionヘッダーフィールドを構築します。
net
LookupMXとResolver.LookupMXは、有効なIPアドレスのように見えるDNS名と有効なドメイン名を返すようになりました。
具体的な変更内容
Go1.24以前では、MXレコードの値として192.168.1.100のようなIPアドレスが返された場合、RFC 5321の厳密な要求に従ってこれらの値は破棄されていました。しかし、実際の運用環境では、一部のネームサーバーや設定でMXレコードにIPアドレスが設定されることがあります。
返される値の例
// Go1.25での動作例
mx, err := net.LookupMX("example.com")
if err != nil {
    log.Fatal(err)
}
for _, record := range mx {
    fmt.Printf("Priority: %d, Host: %s\n", record.Pref, record.Host)
}
// 出力例(Go1.25では以下の両方が返される可能性)
// Priority: 10, Host: mail.example.com       // 従来通りの有効なドメイン名
// Priority: 20, Host: 203.0.113.50          // 新たに許可されるIPアドレス形式
この変更により、RFC要求よりも実用性を重視し、実際のインフラで動作しているメールシステムとの互換性が向上します。
WindowsでLookupMXは、ListenMulticastUDPがIPv6アドレスをサポートするようになりました。
Windowsでは、os.Fileとネットワーク接続間の変換が可能になりました。具体的には、FileConn、FilePacketConn、FileListener関数が実装され、開いているファイルに対応するネットワーク接続またはリスナーを返します。
同様に、TCPConn、UDPConn、UnixConn、IPConn、TCPListener、UnixListenerのFileメソッドが実装され、ネットワーク接続の基礎となるos.Fileを返します。
net/http
新しいCrossOriginProtectionは、安全でないクロスオリジンブラウザのリクエストを拒否することにより、Cross-Site Request Forgery(CSRF)に対する保護を実装します。
これはモダンブラウザのFetchメタデータを使用し、トークンやクッキーを必要とせず、オリジンベースとパターンベースのバイパスをサポートします。
使用例
package main
import (
    "fmt"
    "net/http"
)
func main() {
    // 基本的なCSRF保護
    // APIエンドポイントを保護
    protection := http.CrossOriginProtection{}
    http.Handle("/api/", protection.Wrap(http.HandlerFunc(apiHandler)))
    
    // 管理者エンドポイントを保護
    // 特定のオリジンを許可する設定
    protectionWithAllowlist := http.CrossOriginProtection{
        AllowedOrigins: []string{
            "https://trusted-app.example.com",
            "https://*.internal.company.com", // パターンベース
        },
    }
    http.Handle("/admin/", protectionWithAllowlist.Wrap(http.HandlerFunc(adminHandler)))
    
    // 開発環境用の設定(localhostを許可)
    devProtection := http.CrossOriginProtection{
        AllowedOrigins: []string{
            "http://localhost:*", // 任意のlocalhostポートを許可
        },
    }
    http.Handle("/dev-api/", devProtection.Wrap(http.HandlerFunc(devHandler)))
    
    http.ListenAndServe(":8080", nil)
}
func apiHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Secure API endpoint accessed safely")
}
func adminHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Admin function executed")
}
func devHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Development API")
}
動作の仕組み
- 
自動判定: ブラウザが送信する
Sec-Fetch-Siteヘッダーを確認- 
same-origin: 同一オリジンからのリクエスト → 許可 - 
cross-site: クロスサイトリクエスト → 検証または拒否 - 
none: 直接アクセス(ブックマークなど) → 許可 
 - 
 - 設定不要: 従来のCSRFトークンやクッキーの設定が不要
 - 柔軟な制御: 必要に応じて特定のオリジンを許可リストに追加可能
 
この機能により、モダンなブラウザでのCSRF攻撃を簡単かつ効果的に防ぐことができます。
os
Windowsでは、NewFileが非同期I/O用に開かれたハンドルをサポートするようになりました(つまり、syscall.CreateFile呼び出しでsyscall.FILE_FLAG_OVERLAPPEDが指定されている)。
これらのハンドルはGoランタイムのI/O完了ポートに関連付けられ、結果として得られるFileに以下の利点を提供します。
- I/Oメソッド(
File.Read、File.Write、File.ReadAt、File.WriteAt)がOSスレッドをブロックしません。 - デッドラインメソッド(
File.SetDeadline、File.SetReadDeadline、File.SetWriteDeadline)がサポートされます。 
この機能強化は、Windowsで名前付きパイプを介して通信するアプリケーションに特に有益です。
ハンドルは一度に1つの完了ポートにのみ関連付けることができることに注意してください。
NewFileに提供されたハンドルがすでに完了ポートに関連付けられている場合、返されるFileは同期I/Oモードにダウングレードされます。
この場合、I/OメソッドはOSスレッドをブロックし、デッドラインメソッドは効果がありません。
DirFSとRoot.FSによって返されるfs.FSは、新しいio/fs.ReadLinkFSインターフェースを実装します。
CopyFSは、io/fs.ReadLinkFSを実装するfs.FSをコピーする際にシンボリックリンクをサポートします。
Root型は以下の追加メソッドをサポートします。
Root.ChmodRoot.ChownRoot.ChtimesRoot.LchownRoot.LinkRoot.MkdirAllRoot.ReadFileRoot.ReadlinkRoot.RemoveAllRoot.RenameRoot.SymlinkRoot.WriteFile
reflect
新しいTypeAssert関数は、Valueを指定された型のGoの値に直接変換することを許容します。これはValue.Interfaceの結果に型アサーションを使用するのと似ていますが、不要なメモリ割り当てを避けます。
regexp/syntax
\p{name}と\P{name}文字クラス構文(Unicode文字クラスエスケープ)は、Any、ASCII、Assigned、Cn、LC、および\p{Letter}(\pL用)などのUnicodeカテゴリエイリアスの名前を受け入れるようになりました。
Unicode TR18に従い、スペース、アンダースコア、ハイフンを無視して、大文字小文字を区別しない名前検索も使用するようになりました。
runtime
AddCleanupによってスケジュールされたクリーンアップ関数は、並行・並列に実行されるようになり、uniqueパッケージのように過度にしようする場合でも効率的に実行可能になりました。個々のクリーンアップ処理が長時間実行またはブロックされて、クリーンアップキューのブロックが発生するのを避けるために、その処理は新しいgoroutineに移されるようになるはずです。
新しいGODEBUG=checkfinalizers=1設定は、GCガイドで説明されているようなファイナライザーとクリーンアップの一般的な問題を検出するのに役立ちます。
このモードでは、ランタイムは各ガベージコレクションサイクルで診断を行い、長時間実行される問題のあるファイナライザーやクリーンアップを特定するのに役立つよう、ファイナライザーとクリーンアップキューの長さをstderrに定期的に出力します。
詳細については、GODEBUGドキュメントを参照してください。
新しいSetDefaultGOMAXPROCS関数は、GOMAXPROCSが設定されていない場合のデフォルト値を設定します。
runtime/pprof
ランタイム内部のロックの競合に関するmutexプロファイルは、遅延を引き起こしたクリティカルセクションの終了を正しく指すようになりました。これはsync.Mutexの競合に対するプロファイルの動作と一致します。
sync
新しいWaitGroup.Goメソッドは、複数のgoroutineを作成してそれらの終了を待つ一般的なパターンをより便利にします。
Go1.24以前の従来の方法
func processItems(items []string) {
    var wg sync.WaitGroup
    
    for _, item := range items {
        wg.Add(1)           // 手動でカウンタを増加
        go func(item string) {
            defer wg.Done() // 手動でカウンタを減少
            process(item)
        }(item)
    }
    
    wg.Wait()
}
Go1.25の新しい方法
func processItems(items []string) {
    var wg sync.WaitGroup
    
    for _, item := range items {
        wg.Go(func() { // Add(1), Done()が自動的に呼ばれる
            process(item)
        })
    }
    
    wg.Wait()
}
testing
新しいメソッドT.Attr、B.Attr、F.Attrは、テストログに属性を出力します。属性は、テストに関連付けられた任意のkey-valueです。
例えば、TestFという名前のテストで、t.Attr("key", "value")の出力は以下のようになります。
=== ATTR  TestF key value
-jsonフラグを使用すると、属性は新しい「attr」アクションとして出力されます。
T、B、Fの新しいOutputメソッドは、TB.Logと同様にテストの出力に書き込めるio.Writerを提供します。
TB.Logと同様に、出力はインデントされますが、ファイル名と行番号は含まれません。
AllocsPerRun関数は、並列テストが実行されている場合にパニックするようになりました。
AllocsPerRunの結果は、他のテストが実行されている場合、本質的に不安定です。
新しいパニック動作は、このようなバグを検知するのに役立ちます。
testing/fstest
MapFSはio/fs.ReadLinkFSインターフェースを実装します。
TestFSは、io/fs.ReadLinkFSインターフェースを実装している場合、Lstatの値を見るようになります。
TestFSは、無限に再帰することを避けるためにシンボリックリンクを追跡しなくなりました。
unicode
新しいCategoryAliasesマップは、「L」に対する「Letter」などのカテゴリエイリアス名への変換手段を提供します。
新しいカテゴリCnとLCは、それぞれ未割り当てのコードポイントと大文字小文字のある文字を定義します。
これらは常にUnicodeによって定義されていましたが、Go1.25以前では誤って省略されていました。
Cカテゴリは現在Cnを含むため、すべての未割り当てのコードポイントが追加されています。
unique
uniqueパッケージは、インターン化された値をより積極的に、効率的に、並列に回収するようになりました。その結果、Make関数を使用するアプリケーションは、ユニークな値が多数インターン化された場合に、メモリ使用量の急激な増加が起きにくくなりました。
Handleを含むMake関数に渡された値は、ガベージコレクションでの回収時に、値の階層の深さに比例したサイクル数を要していました。Go1.25からは、未使用になると、1回のサイクルですぐに回収されます。
ports
Darwin
Go1.24のリリースノートに書かれていた通り、Go1.25はmacOS 12 Monterey以降をサポート対象とします。
Windows
Go1.25は、不具合のある 32ビットwindows/armポート(GOOS=windows GOARCH=arm)を含む最後のリリースです。Go1.26で削除される予定です。
Loong64
linux/loong64ポートは、race detector、runtime.SetCgoTracebackを使用したCコードからのトレースバック情報の収集、および内部リンクモードでのcgoプログラムのリンクをサポートするようになりました。
RISC-V
linux/riscv64ポートでpluginビルドモードをサポートするようになりました。
GORISCV64環境変数に新しい値rva23u64を設定することで、RVA23U64プロファイルの命令セットを利用できるようになります。
Discussion