os/exec: add Cmd.Clone method
要約
概要
os/exec.Cmd に対して Clone() メソッドを追加することで、同一コマンドの設定を再利用して新しい Cmd インスタンスを安全に作成できるようにするという提案です。提案者自身が設計上の問題点を認識し最終的に撤回したため、「declined as retracted(撤回による却下)」となりました。
ステータス変更
active → declined
提案者の @adonovan 氏が「Cmd は設定フィールドと実行状態が混在した構造体であり、Clone メソッドはむしろ問題の温床になる」と結論付け、2026年5月13日に自ら提案を撤回しました。翌日、@aclements 氏がプロポーザルレビューグループを代表して「declined as retracted」として正式にクローズしました。
技術的背景
現状の問題点
Go 1.26に向けて、exec.Cmd の Start メソッドを2回呼び出した際の誤用検出が強化されました(CL 728642、関連: #76746)。この変更で内部フィールドに sync/atomic のbool値が追加されたため、go vet の copylocks 静的解析チェッカーが Cmd の値コピーに対して警告を出すようになりました。
Start 失敗後に同じ Cmd を再利用したいケース(例: 一時的なエラーからのリトライ)では、安全な回避策として新しい Cmd を手動で構築する必要がありました。
// 問題のある従来のコード(Start失敗後に再利用しようとする)
cmd := exec.Command("myapp", args...)
cmd.Env = append(os.Environ(), "FOO=bar")
err := cmd.Start()
if err != nil {
// Startに失敗した場合でも同じcmdを再利用しようとすると問題が発生
err = cmd.Start() // エラー: exec: already started
}
// 現在のワークアラウンド(フラジルな手動コピー)
cmd2 := &exec.Cmd{
Path: cmd.Path,
Args: cmd.Args,
Env: cmd.Env,
// ...全エクスポートフィールドを列挙する必要あり
// 新フィールド追加時に対応漏れが生じやすい
// さらにctxフィールドはエクスポートされておらず、コピー不可能
}
提案された解決策
package exec // "os/exec"
// Clone returns a new Cmd that is a copy of the old one with respect to
// its configuration, and on which Start has never been called.
func (*Cmd) Clone() *Cmd
全エクスポートフィールドと、CommandContext で設定される非エクスポートの ctx フィールドを含めた設定情報をコピーし、Start が呼ばれていない新しい Cmd を返します。net/http.Request.Clone に類似したパターンを想定していました。
これによって何ができるようになるか
この提案が実現していた場合の想定ユースケースは以下の通りです(実際には採用されませんでした)。
- コマンドのリトライ: 一時的な失敗後に同一設定で再実行
- 共通設定の雛形: ベースとなる
Cmdから複数のバリアントを派生 - テンプレート的な使用: 共通の環境変数・パスを設定済みの
Cmdを複製
議論のハイライト
atomic.Boolの使用が直接のきっかけ:Startの多重呼び出し検出のために内部でsync/atomicを使ったところ、copylocksチェッカーが既存のコードに警告を出し始めた。aclementsの提案で後にatomic.Boolから通常のboolに戻し(CL 734200)、警告問題は切り離された。Std{in,out,err}フィールドの扱いが未解決の核心問題:Cloneが全フィールドをコピーする場合、bytes.Bufferは既に書き込まれた内容を引き継ぐ、パイプは元のCmdの状態に依存する、strings.Readerは巻き戻されないなど、I/O関連フィールドの意味論が複雑すぎると判明。- コンテキストのコピー問題: 非エクスポートの
ctxフィールドをコピーする場合、最初のCmdがコンテキストのタイムアウトで失敗した際、新しいCmdにそのキャンセル済みコンテキストが引き継がれてしまう。かといってClone(ctx context.Context)のようにパラメータを取ると、元のコンテキストを再利用したいケースに対応できない。 Cmdは設定と実行状態の混合体:adonovan氏の最終結論は「Cmdが設定と実行中の状態を混在させている構造体である以上、Cloneメソッドは問題の温床(can of worms)になる」というものであった。- 代替アプローチへの方向転換: 提案者は最終的に、「
Cmdを非コピー可能として明示する」「新しいCmdの生成ロジックはアプリケーション側でファクトリ関数として分離する」という方針を推奨して提案を撤回した。