Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 12031] ファイルの概要

このコミットは、Go言語のosパッケージにおけるLinkおよびSymlink関数が返すエラーの型について、ドキュメントを改善するものです。具体的には、これらの関数が*LinkError型のエラーを返すことを明示的に記述し、file_plan9.goにおいてはLinkErrorを実際に返すように修正しています。これにより、APIの利用者がエラーハンドリングをより正確に行えるようになります。

コミット

commit a5f21c95dc279f421c094a592d65bf2ef89e87d6
Author: Brad Fitzpatrick <bradfitz@golang.org>
Date:   Sat Feb 18 04:45:43 2012 -0800

    os: document the type of link errors
    
    R=golang-dev, r
    CC=golang-dev
    https://golang.org/cl/5673090
---
 src/pkg/os/file_plan9.go | 8 +++++---\n src/pkg/os/file_posix.go | 2 ++\n 2 files changed, 7 insertions(+), 3 deletions(-)\n
diff --git a/src/pkg/os/file_plan9.go b/src/pkg/os/file_plan9.go
index 70041f22aa..cb0e9ef928 100644
--- a/src/pkg/os/file_plan9.go
+++ b/src/pkg/os/file_plan9.go
@@ -276,7 +276,6 @@ func Remove(name string) error {
 }\n \n // Rename renames a file.\n-// If there is an error, it will be of type *PathError.\n func Rename(oldname, newname string) error {\n \tvar d Dir\n \td.Null()\n@@ -340,12 +339,15 @@ func Pipe() (r *File, w *File, err error) {
 // not supported on Plan 9\n \n // Link creates a hard link.\n+// If there is an error, it will be of type *LinkError.\n func Link(oldname, newname string) error {\n-\treturn ErrPlan9\n+\treturn &LinkError{\"link\", oldname, newname, ErrPlan9}\n }\n \n+// Symlink creates newname as a symbolic link to oldname.\n+// If there is an error, it will be of type *LinkError.\n func Symlink(oldname, newname string) error {\n-\treturn ErrPlan9\n+\treturn &LinkError{\"symlink\", oldname, newname, ErrPlan9}\n }\n \n func Readlink(name string) (string, error) {\ndiff --git a/src/pkg/os/file_posix.go b/src/pkg/os/file_posix.go
index 8861af1c7d..2ffc2ee083 100644
--- a/src/pkg/os/file_posix.go
+++ b/src/pkg/os/file_posix.go
@@ -38,6 +38,7 @@ func (e *LinkError) Error() string {
 }\n \n // Link creates newname as a hard link to the oldname file.\n+// If there is an error, it will be of type *LinkError.\n func Link(oldname, newname string) error {\n \te := syscall.Link(oldname, newname)\n \tif e != nil {\n@@ -47,6 +48,7 @@ func Link(oldname, newname string) error {
 }\n \n // Symlink creates newname as a symbolic link to oldname.\n+// If there is an error, it will be of type *LinkError.\n func Symlink(oldname, newname string) error {\n \te := syscall.Symlink(oldname, newname)\n \tif e != nil {\n```

## GitHub上でのコミットページへのリンク

[https://github.com/golang/go/commit/a5f21c95dc279f421c094a592d65bf2ef89e87d6](https://github.com/golang/go/commit/a5f21c95dc279f421c094a592d65bf2ef89e87d6)

## 元コミット内容

os: document the type of link errors

R=golang-dev, r CC=golang-dev https://golang.org/cl/5673090


## 変更の背景

Go言語の`os`パッケージは、ファイルシステム操作のための基本的な機能を提供します。これらの関数は、操作が失敗した場合にエラーを返します。Goのエラーハンドリングの慣習として、特定の操作で発生しうるエラーの型をドキュメントに明記することは、APIの利用者がエラーの種類に応じて適切な処理(例えば、エラーの型アサーションやエラー値の比較)を行う上で非常に重要です。

このコミットの背景には、`os`パッケージの`Link`(ハードリンクの作成)および`Symlink`(シンボリックリンクの作成)関数が返すエラーの型が、ドキュメント上で明確にされていなかったという問題があります。既存の`Rename`関数には`*PathError`を返すことが明記されていましたが、リンク関連の関数には同様の記述がありませんでした。

特に、`file_plan9.go`の実装では、`Link`と`Symlink`が単に`ErrPlan9`という汎用的なエラーを返していました。これは、Plan 9システムではこれらの操作がサポートされていないことを示すものでしたが、Goのエラーハンドリングのベストプラクティスに従うと、ファイルパスに関連するエラーは`*PathError`または`*LinkError`のような構造化されたエラー型でラップされるべきです。これにより、エラーが発生した操作(`"link"`や`"symlink"`)、関連するパス(`oldname`, `newname`)、そして根本的なエラー(`ErrPlan9`)といった詳細情報をエラーオブジェクトに含めることができます。

このコミットは、これらの関数が`*LinkError`型のエラーを返すことをドキュメントに明記し、さらにPlan 9の実装においても`*LinkError`を適切に返すように修正することで、APIの一貫性とエラーハンドリングの利便性を向上させることを目的としています。

## 前提知識の解説

### Go言語のエラーハンドリング

Go言語では、エラーは組み込みの`error`インターフェースによって表現されます。関数がエラーを返す場合、通常は最後の戻り値として`error`型を返します。
```go
type error interface {
    Error() string
}

エラーハンドリングは、通常if err != nilという形式で行われます。

特定の種類のエラーを区別するために、Goではカスタムエラー型を定義し、errorインターフェースを実装させることが一般的です。これにより、エラー発生時にそのエラーの具体的な型をチェックし、より詳細な情報を取得したり、エラーの種類に応じた処理を行ったりすることが可能になります。これは、型アサーション(err.(*MyCustomError))や、Go 1.13以降で導入されたerrors.Aserrors.Is関数を用いて行われます。

osパッケージのエラー型

osパッケージは、ファイルシステム操作に関連する特定のエラー型を定義しています。

  • *os.PathError: ファイルパスに関連する操作(例: Open, Stat, Remove, Renameなど)でエラーが発生した場合に返されるエラー型です。この構造体は、エラーが発生した操作名(Op)、関連するファイルパス(Path)、そして根本的なシステムコールエラー(Err)を含みます。

    type PathError struct {
        Op   string // 操作 (例: "open", "unlink")
        Path string // 操作対象のパス
        Err  error  // 根本的なエラー
    }
    func (e *PathError) Error() string
    
  • *os.LinkError: ハードリンクやシンボリックリンクの作成(Link, Symlink)でエラーが発生した場合に返されるエラー型です。PathErrorと同様に、操作名(Op)、古いパス(Old)、新しいパス(New)、そして根本的なシステムコールエラー(Err)を含みます。このコミット以前はosパッケージのドキュメントで明示的に言及されていませんでしたが、このコミットによってその存在と使用が明確化されました。

    type LinkError struct {
        Op  string // 操作 (例: "link", "symlink")
        Old string // 古いパス
        New string // 新しいパス
        Err error  // 根本的なエラー
    }
    func (e *LinkError) Error() string
    

ハードリンクとシンボリックリンク

  • ハードリンク (Hard Link): 同じiノード(ファイルシステム上のデータの実体)を指す、ファイルへの追加の参照です。元のファイルが削除されても、ハードリンクが存在する限りデータは削除されません。異なるファイルシステムをまたいで作成することはできません。
  • シンボリックリンク (Symbolic Link / Soft Link): 別のファイルやディレクトリへのパスを指す特殊なファイルです。WindowsのショートカットやUnix/Linuxのシンボリックリンクに相当します。元のファイルが削除されると、シンボリックリンクは「壊れたリンク」となり、参照先を見失います。異なるファイルシステムをまたいで作成できます。

Plan 9とPOSIX

Go言語のosパッケージは、様々なオペレーティングシステムに対応するために、OS固有の実装を持っています。

  • POSIX: Portable Operating System Interfaceの略で、Unix系OSの標準インターフェースを定義したものです。Linux、macOS、BSDなどがこれに準拠しています。file_posix.goはこれらのシステム向けの実装を含みます。
  • Plan 9: ベル研究所で開発された分散オペレーティングシステムです。Go言語の開発者の一部はPlan 9の経験があり、Goの設計思想にも影響を与えています。file_plan9.goはPlan 9システム向けの実装を含みます。Plan 9では一部のファイルシステム操作(例: ハードリンク)がサポートされていない場合があります。

技術的詳細

このコミットの主要な技術的詳細は、Go言語のエラーハンドリングにおける「エラーの型を明示する」というベストプラクティスをosパッケージのLinkおよびSymlink関数に適用した点にあります。

GoのAPIドキュメントでは、関数が特定のカスタムエラー型を返す場合、そのことを明記することが推奨されます。これにより、開発者はswitch err.(type)errors.Asなどを用いて、エラーの種類に応じた分岐処理を記述できます。

変更前は、os.Linkos.Symlinkのドキュメントには、エラーが返されることしか書かれていませんでした。しかし、実際にはこれらの関数は*os.LinkErrorを返すことが期待されます。このコミットは、その期待をドキュメントに反映させました。

さらに重要なのは、file_plan9.goにおける実装の変更です。Plan 9システムではハードリンクやシンボリックリンクがサポートされていないため、以前はこれらの関数が単にErrPlan9という事前定義されたエラー値を返していました。しかし、これはLinkErrorという構造化されたエラー型が提供する詳細情報(操作名、古いパス、新しいパス)を失うことになります。

このコミットでは、file_plan9.goLinkおよびSymlink関数が、ErrPlan9を内部エラーとして含む*os.LinkErrorインスタンスを返すように修正されました。

// 変更前 (file_plan9.go)
func Link(oldname, newname string) error {
	return ErrPlan9
}
func Symlink(oldname, newname string) error {
	return ErrPlan9
}

// 変更後 (file_plan9.go)
func Link(oldname, newname string) error {
	return &LinkError{"link", oldname, newname, ErrPlan9}
}
func Symlink(oldname, newname string) error {
	return &LinkError{"symlink", oldname, newname, ErrPlan9}
}

この変更により、Plan 9環境でリンク操作が失敗した場合でも、エラーを処理する側は*os.LinkErrorとしてエラーを受け取り、どの操作("link"または"symlink")が、どのパス(oldname, newname)で失敗し、その根本的な理由がErrPlan9である、という詳細な情報を取得できるようになります。これは、エラーログの改善や、より堅牢なエラーハンドリングロジックの実装に貢献します。

file_posix.goに関しては、既にsyscall.Linksyscall.Symlinkが返すエラーを*os.LinkErrorでラップして返していたため、ドキュメントの追加のみが行われました。これは、POSIXシステムではこれらの操作が通常サポートされており、システムコールが返すエラーをGoの慣習に沿った形でラップすることが適切であるためです。

コアとなるコードの変更箇所

diff --git a/src/pkg/os/file_plan9.go b/src/pkg/os/file_plan9.go
index 70041f22aa..cb0e9ef928 100644
--- a/src/pkg/os/file_plan9.go
+++ b/src/pkg/os/file_plan9.go
@@ -276,7 +276,6 @@ func Remove(name string) error {
 }\n \n // Rename renames a file.\n-// If there is an error, it will be of type *PathError.\n func Rename(oldname, newname string) error {\n \tvar d Dir\n \td.Null()\n@@ -340,12 +339,15 @@ func Pipe() (r *File, w *File, err error) {
 // not supported on Plan 9\n \n // Link creates a hard link.\n+// If there is an error, it will be of type *LinkError.\n func Link(oldname, newname string) error {\n-\treturn ErrPlan9\n+\treturn &LinkError{\"link\", oldname, newname, ErrPlan9}\n }\n \n+// Symlink creates newname as a symbolic link to oldname.\n+// If there is an error, it will be of type *LinkError.\n func Symlink(oldname, newname string) error {\n-\treturn ErrPlan9\n+\treturn &LinkError{\"symlink\", oldname, newname, ErrPlan9}\n }\n \n func Readlink(name string) (string, error) {\ndiff --git a/src/pkg/os/file_posix.go b/src/pkg/os/file_posix.go
index 8861af1c7d..2ffc2ee083 100644
--- a/src/pkg/os/file_posix.go
+++ b/src/pkg/os/file_posix.go
@@ -38,6 +38,7 @@ func (e *LinkError) Error() string {
 }\n \n // Link creates newname as a hard link to the oldname file.\n+// If there is an error, it will be of type *LinkError.\n func Link(oldname, newname string) error {\n \te := syscall.Link(oldname, newname)\n \tif e != nil {\n@@ -47,6 +48,7 @@ func Link(oldname, newname string) error {
 }\n \n // Symlink creates newname as a symbolic link to oldname.\n+// If there is an error, it will be of type *LinkError.\n func Symlink(oldname, newname string) error {\n \te := syscall.Symlink(oldname, newname)\n \tif e != nil {\n```

## コアとなるコードの解説

このコミットは主に2つのファイル、`src/pkg/os/file_plan9.go`と`src/pkg/os/file_posix.go`に変更を加えています。

### `src/pkg/os/file_plan9.go`の変更

1.  **`Rename`関数のコメント削除**:
    `// If there is an error, it will be of type *PathError.`
    この行が削除されています。これは、`Rename`関数が`*PathError`を返すことは既に確立された慣習であり、他の多くのファイル操作関数と同様に自明であるため、冗長な記述と判断された可能性があります。または、このコミットの焦点が`LinkError`のドキュメント化にあるため、関連性の低いコメントを整理したのかもしれません。

2.  **`Link`関数のドキュメントと実装の変更**:
    *   ドキュメントに`// If there is an error, it will be of type *LinkError.`という行が追加されました。これにより、`Link`関数が`*LinkError`を返すことが明示されます。
    *   実装が`return ErrPlan9`から`return &LinkError{"link", oldname, newname, ErrPlan9}`に変更されました。これは、Plan 9システムでハードリンクがサポートされていない場合でも、`LinkError`構造体を使ってエラーの詳細情報(操作名、古いパス、新しいパス、そして根本的なエラー`ErrPlan9`)をカプセル化して返すようにしたものです。これにより、エラーハンドリングの際に、より豊富なコンテキストが得られるようになります。

3.  **`Symlink`関数のドキュメントと実装の変更**:
    *   `Link`関数と同様に、ドキュメントに`// If there is an error, it will be of type *LinkError.`が追加されました。
    *   実装が`return ErrPlan9`から`return &LinkError{"symlink", oldname, newname, ErrPlan9}`に変更されました。これも`Link`関数と同様に、Plan 9システムでシンボリックリンクがサポートされていない場合でも、`LinkError`構造体を使ってエラーの詳細情報を返すようにしたものです。

### `src/pkg/os/file_posix.go`の変更

1.  **`Link`関数のドキュメント追加**:
    `// If there is an error, it will be of type *LinkError.`という行が追加されました。POSIXシステムでは、`Link`関数は内部で`syscall.Link`を呼び出し、その結果のエラーを`*LinkError`でラップして返していました。この変更は、その既存の振る舞いをドキュメントに明記したものです。

2.  **`Symlink`関数のドキュメント追加**:
    `Link`関数と同様に、`// If there is an error, it will be of type *LinkError.`という行が追加されました。`Symlink`関数も内部で`syscall.Symlink`を呼び出し、その結果のエラーを`*LinkError`でラップして返していたため、その振る舞いをドキュメントに反映させたものです。

これらの変更により、`os`パッケージの`Link`および`Symlink`関数は、どのOSの実装においても一貫して`*os.LinkError`を返すことがドキュメントで保証され、Plan 9の実装においてもその保証がコードレベルで実現されました。これにより、GoのファイルシステムAPIの堅牢性と使いやすさが向上しています。

## 関連リンク

*   Go言語 `os` パッケージドキュメント: [https://pkg.go.dev/os](https://pkg.go.dev/os)
*   `os.Link` 関数ドキュメント: [https://pkg.go.dev/os#Link](https://pkg.go.dev/os#Link)
*   `os.Symlink` 関数ドキュメント: [https://pkg.go.dev/os#Symlink](https://pkg.go.dev/os#Symlink)
*   `os.PathError` 型ドキュメント: [https://pkg.go.dev/os#PathError](https://pkg.go.dev/os#PathError)
*   `os.LinkError` 型ドキュメント: [https://pkg.go.dev/os#LinkError](https://pkg.go.dev/os#LinkError)
*   Go言語のエラーハンドリングに関する公式ブログ記事 (A Tour of Go - Errors): [https://go.dev/tour/basics/16](https://go.dev/tour/basics/16)

## 参考にした情報源リンク

*   Go言語の公式ドキュメント (`pkg.go.dev`)
*   Go言語のソースコード (`github.com/golang/go`)
*   Go言語のエラーハンドリングに関する一般的な知識
*   ハードリンクとシンボリックリンクに関する一般的な知識
*   Plan 9オペレーティングシステムに関する一般的な知識