[インデックス 12265] ファイルの概要
このコミットは、Go言語の公式ドキュメントの一つである「Go For C++ Programmers」を更新し、現在のGo言語の慣用的な記述やプラクティスに沿うように修正を加えるものです。主に、コード例のクリーンアップ、Go言語の機能に関する説明の明確化、そしてGoの命名規則やメモリ管理に関するより現代的なアプローチを反映しています。
コミット
commit b69fa69a8bf53fb4714d96b4daf80fd6de597111
Author: Ian Lance Taylor <iant@golang.org>
Date: Tue Feb 28 16:49:57 2012 -0800
doc: update Go For C++ Programmers
Minor cleanups to look more like current Go.
R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5707053
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/b69fa69a8bf53fb4714d96b4daf80fd6de597111
元コミット内容
doc: update Go For C++ Programmers
Minor cleanups to look more like current Go.
変更の背景
このコミットの背景には、Go言語が進化し、その慣用的な記述方法や推奨されるプラクティスが確立されてきたことがあります。特に、C++プログラマー向けにGo言語の概念を説明するドキュメントは、Go言語の初期のバージョンで書かれていたため、時間の経過とともに一部の内容が古くなったり、現在のGoのベストプラクティスと乖離したりしていました。
具体的には、以下の点が主な変更の動機となっています。
- 命名規則の統一: Go言語では、エクスポートされる(パッケージ外からアクセス可能な)識別子(関数、メソッド、型、フィールドなど)は先頭が大文字でなければならないという厳格な規則があります。しかし、古いドキュメントのコード例では、この規則が守られていない箇所がありました。これを修正し、よりGoらしいコード例を提供することが求められました。
- メモリ管理の慣用的な記述: Goにはガベージコレクションがありますが、
new
キーワードの使用方法や、構造体の初期化における&
演算子と複合リテラル(&S{0}
のような形式)の利用が、C++のnew
とは異なる慣用的なパターンとして確立されていました。この違いをC++プログラマーに正確に伝える必要がありました。 - 標準ライブラリの利用: Go言語の標準ライブラリは非常に充実しており、
sys
パッケージのような低レベルなAPIではなく、time
やfmt
といったより高レベルで慣用的なパッケージを使用することが推奨されています。古いコード例がこれらの現代的な利用法を反映していなかったため、更新が必要でした。 - ドキュメントの可読性向上: HTMLドキュメントの構造を改善し、コードブロックや説明文の周囲に適切な
<p>
タグを追加することで、視覚的な区切りを明確にし、全体的な可読性を向上させる狙いがありました。 - Go言語の進化の反映: スライス、インターフェース、ゴルーチン、チャネルなどのGo言語のコア機能に関する説明も、より正確で現代的な表現に更新されました。特に、ガベージコレクションの説明から「incremental and highly efficient on modern processors」という具体的な実装に関する記述を削除し、より抽象的な説明にすることで、将来的な実装変更にも対応できるようにしています。
これらの変更は、C++プログラマーがGo言語を学習する際に、最新かつ最も慣用的なGoの知識を習得できるようにすることを目的としています。
前提知識の解説
このコミットの変更内容を理解するためには、Go言語の基本的な概念と、C++との比較におけるいくつかの重要な違いについて知っておく必要があります。
-
Go言語の命名規則 (Exported Identifiers):
- Go言語では、識別子(変数、関数、メソッド、型、フィールドなど)の先頭の文字が大文字であるか小文字であるかによって、その可視性(スコープ)が決定されます。
- 大文字で始まる識別子: その識別子が定義されているパッケージの外部からもアクセス可能です("exported")。これはC++の
public
メンバーに似ています。 - 小文字で始まる識別子: その識別子が定義されているパッケージ内でのみアクセス可能です("unexported")。これはC++の
private
またはprotected
メンバーに似ています。 - このコミットでは、
get
/set
のようなメソッド名がGet
/Set
に変更されていますが、これはこれらのメソッドがインターフェースの一部としてエクスポートされるべきであるため、Goの命名規則に従ったものです。
-
Go言語のメモリ管理 (
new
と&
、複合リテラル):- Goにはガベージコレクションがあり、C++のように手動でメモリを解放する必要はありません。
new
関数: Goのnew
はC++のnew
演算子とは異なり、型を受け取り、その型のゼロ値に初期化された新しい項目へのポインタを返します。例えば、new(int)
は*int
型で、値が0
のint
へのポインタを返します。&
演算子と複合リテラル: Goでは、既存の変数のアドレスを取得するために&
演算子を使用します。また、構造体や配列、スライスなどを初期化する際に「複合リテラル」という構文を使用します。例えば、&S{0}
は、構造体S
のインスタンスを初期化し、そのアドレス(ポインタ)を返します。Goでは、new(S)
よりも&S{}
や&S{フィールド名: 値}
のような複合リテラルを使う方が一般的で慣用的です。これは、変数のアドレスを明示的に取得しても、必要に応じてヒープに割り当てられるため、ダングリングポインタ(無効なポインタ)が発生しないというGoの特性に基づいています。
-
Go言語の標準ライブラリ:
- Goは非常に強力な標準ライブラリを持っています。
fmt
パッケージ: フォーマットされたI/O(入出力)を提供します。fmt.Print
やfmt.Println
は、C++のstd::cout
に相当する、標準出力への出力によく使われる関数です。time
パッケージ: 時間に関する機能を提供します。time.Sleep
は、指定された期間、現在のゴルーチンをスリープさせるために使用されます。これは、C++でスレッドをスリープさせるためにプラットフォーム固有のAPI(例:Sleep
on Windows,usleep
on Unix-like systems)を使用するのと似ていますが、Goでは標準ライブラリで提供されています。
-
Go言語の構文と慣用句:
- 短い変数宣言 (
:=
): 関数内で変数を宣言し、初期化する際にvar
キーワードと型を省略して:=
演算子を使用する、Goで非常に頻繁に使われる構文です。 - 複数代入: Goでは、
i, j = j, i
のように複数の変数に同時に値を代入できます。これは値の交換などによく使われます。 - 暗黙のセミコロン挿入: Goのパーサーは、特定の場所で改行があった場合に自動的にセミコロンを挿入します。これにより、C++のように明示的にセミコロンを記述する機会が減りますが、改行の位置によっては構文エラーになる場合があります(例:
if x { } else { }
のelse
が前の行と同じ行にない場合)。 - スライス: Goのスライスは、配列の一部を参照する動的なビューです。C++のポインタと配列を組み合わせたような概念ですが、より安全で柔軟です。
a[i:j]
のような構文で作成され、len
(長さ)とcap
(容量)を持ちます。
- 短い変数宣言 (
これらの前提知識を理解することで、コミットで行われた変更がなぜ行われたのか、そしてそれがGo言語の哲学や慣用的な記述にどのように合致するのかを深く理解することができます。
技術的詳細
このコミットにおける技術的な変更は、主にdoc/go_for_cpp_programmers.html
ファイル内のGo言語のコード例と説明文の更新に集中しています。これらの変更は、Go言語の進化と、より慣用的なプログラミングスタイルへの移行を反映しています。
-
HTML構造のクリーンアップと可読性向上:
- 多くのセクションで、コードブロックや説明文の周囲に
<p>
タグが追加されています。これは、HTMLドキュメントのセマンティックな構造を改善し、ブラウザでのレンダリング時に適切な段落区切りを提供することで、ドキュメント全体の可読性を向上させるためのものです。 - 例えば、Goの宣言構文、変数初期化、複数代入、セミコロンのルール、
gofmt
の推奨、ポインタの利用、制御構造、defer
、定数、スライス、new
関数、インターフェース、ゴルーチン、チャネルなど、ほぼ全ての主要なセクションでこの変更が適用されています。
- 多くのセクションで、コードブロックや説明文の周囲に
-
ガベージコレクションの説明の簡素化:
- 変更前:
The garbage collection is (intended to be) incremental and highly efficient on modern processors.
- 変更後:
to release memory explicitly.
(この部分が削除され、より簡潔な説明になった) - これは、ガベージコレクションの実装詳細に深く踏み込むことを避け、Goのガベージコレクションが「明示的なメモリ解放を不要にする」という本質的な機能に焦点を当てるための変更です。実装の詳細は時間の経過とともに変化する可能性があるため、ドキュメントをより普遍的なものにする意図があります。
- 変更前:
-
変数宣言と初期化の例の更新:
- 短い変数宣言
:=
の例が追加され、var v = *p
とv := *p
が並列で示されるようになりました。これは、Goにおいて関数内で変数を宣言・初期化する際の最も一般的な慣用句が:=
であることを強調しています。 - ゼロ値初期化の説明がより明確に
(<code>0</code>, <code>nil</code>, etc.)
と記述されました。
- 短い変数宣言
-
セミコロン挿入ルールの明確化:
- Goの自動セミコロン挿入のルールについて、C++プログラマーが陥りやすい間違い(例:
func g()\n{}
やif x {}\nelse {}
)を具体的に示し、それがなぜ構文エラーになるのかを説明する段落が追加されました。これは、Goの構文解析の挙動をより深く理解させるための重要な追加です。
- Goの自動セミコロン挿入のルールについて、C++プログラマーが陥りやすい間違い(例:
-
スライス構文の拡張と明確化:
- スライスの作成構文
a[I:J]
がa[i:j]
に変更され、i
やj
が省略された場合の挙動(i
が省略された場合は0
から、j
が省略された場合はlen(a)
まで)が明示的に説明されました。 - 配列全体をスライスとして関数に渡す際の慣用句として
a[:]
が導入されました。これは、C++で配列をポインタと長さで渡すのと異なり、Goではスライスがより安全で効率的な方法であることを示しています。
- スライスの作成構文
-
new
関数と複合リテラルの詳細な説明:- このコミットの最も重要な技術的変更の一つは、
new
関数の使用がGoプログラムでは一般的ではないことを説明し、代わりに複合リテラル(例:&S{0}
)が推奨されることを示す新しいセクションが追加されたことです。 new(S)
、var s S; return &s
、&S{0}
の3つの関数f1
,f2
,f3
が同等であることを示し、特にf3
が最も慣用的であると説明しています。これは、Goのコンパイラが変数のエスケープ解析を行い、必要に応じてヒープに割り当てるため、スタックに割り当てられた変数のアドレスを返しても安全であるというGoのメモリモデルの理解を深めます。
- このコミットの最も重要な技術的変更の一つは、
-
命名規則の適用 (大文字化):
- メソッド名、インターフェース名、構造体フィールド名、関数名など、エクスポートされるべき識別子の先頭が小文字から大文字に変更されました。
get
/set
->Get
/Set
(メソッド名)myInterface
->myPrintInterface
(インターフェース名)iterator
->Iterator
(インターフェース名)cmd
->Cmd
(構造体名)manager
->Manager
(関数名)get
/val
->Get
/Val
(構造体フィールド名)
- これは、Goの命名規則(エクスポートされる識別子は先頭が大文字)に厳密に従うための修正であり、Goコミュニティにおけるベストプラクティスを反映しています。
- メソッド名、インターフェース名、構造体フィールド名、関数名など、エクスポートされるべき識別子の先頭が小文字から大文字に変更されました。
-
標準ライブラリの更新:
- ゴルーチンの例で、
sys.sleep(10)
がtime.Sleep(10 * time.Second)
に、print(i)
がfmt.Print(i)
にそれぞれ変更されました。これは、Goの標準ライブラリの進化と、より現代的で推奨されるAPIの使用を反映しています。sys
パッケージはGoの初期段階で存在しましたが、後にtime
やfmt
などのより特化したパッケージに置き換えられました。
- ゴルーチンの例で、
-
iota
の例の追加:iota
キーワードを使った定数宣言の例が追加されました。iota
はGoで列挙型のような連続する定数を定義する際によく使われる特殊な定数ジェネレータです。
これらの技術的変更は、Go言語のドキュメントを最新の状態に保ち、C++プログラマーがGoの慣用的なスタイルとベストプラクティスをより正確に理解できるようにすることを目的としています。
コアとなるコードの変更箇所
このコミットは、doc/go_for_cpp_programmers.html
という単一のHTMLドキュメントファイルを変更しています。以下に、その主要な変更箇所を抜粋し、その意図を説明します。
-
ガベージコレクションの説明の簡素化:
--- a/doc/go_for_cpp_programmers.html +++ b/doc/go_for_cpp_programmers.html @@ -29,8 +32,7 @@ For a detailed description of the Go language, see the Interfaces are also used where C++ uses templates. <li>Go uses garbage collection. It is not necessary (or possible) - to release memory explicitly. The garbage collection is (intended to be) - incremental and highly efficient on modern processors. + to release memory explicitly. <li>Go has pointers but not pointer arithmetic. You cannot use a pointer variable to walk through the bytes of a string.
- 変更点: ガベージコレクションの「incremental and highly efficient on modern processors」という具体的な実装に関する記述が削除されました。
- 意図: 実装の詳細に依存しない、より普遍的な説明にするため。
-
new
関数と複合リテラルの説明の追加:--- a/doc/go_for_cpp_programmers.html +++ b/doc/go_for_cpp_programmers.html @@ -408,6 +443,33 @@ initializes it with the value `0`, and returns its address, which has type `*int`. Unlike in C++, `new` is a function, not an operator; `new int` is a syntax error. +</p> + +<p> +Perhaps surprisingly, `new` is not commonly used in Go +programs. In Go taking the address of a variable is always safe and +never yields a dangling pointer. If the program takes the address of +a variable, it will be allocated on the heap if necessary. So these +functions are equivalent: +</p> + +<pre> +type S { I int } + +func f1() *S { + return new(S) +} + +func f2() *S { + var s S + return &s +} + +func f3() *S { + // More idiomatic: use composite literal syntax. + return &S{0} +} +</pre> <p> Map and channel values must be allocated using the builtin function
- 変更点:
new
関数がGoではあまり使われないこと、&
演算子と複合リテラルを使った構造体初期化がより慣用的であること、そしてそれらが同等であることを示す新しいコード例と説明が追加されました。 - 意図: C++の
new
との違いを明確にし、Goにおけるメモリ割り当ての慣用的なパターンを教えるため。
- 変更点:
-
メソッド名のGo慣用句への変更 (大文字化):
--- a/doc/go_for_cpp_programmers.html +++ b/doc/go_for_cpp_programmers.html @@ -447,33 +511,38 @@ Go, any type which provides the methods named in the interface may be treated as an implementation of the interface. No explicitly declared inheritance is required. The implementation of the interface is entirely separate from the interface itself. +</p> <p> A method looks like an ordinary function definition, except that it has a <em>receiver</em>. The receiver is similar to the `this` pointer in a C++ class method. +</p> <pre> type myType struct { i int } -func (p *myType) get() int { return p.i } +func (p *myType) Get() int { return p.i } </pre> <p> -This declares a method `get` associated with `myType`. +This declares a method `Get` associated with `myType`. The receiver is named `p` in the body of the function. +</p> <p> Methods are defined on named types. If you convert the value to a different type, the new value will have the methods of the new type, not the old type. +</p> <p> You may define methods on a builtin type by declaring a new named type derived from it. The new type is distinct from the builtin type. +</p> <pre> type myInteger int -func (p myInteger) get() int { return int(p) } // Conversion required. +func (p myInteger) Get() int { return int(p) } // Conversion required. func f(i int) { } var v myInteger // f(v) is invalid. @@ -482,58 +551,64 @@ var v myInteger <p> Given this interface: +</p> <pre> type myInterface interface { - get() int - set(i int) + Get() int + Set(i int) } </pre> <p> we can make `myType` satisfy the interface by adding +</p> <pre> -func (p *myType) set(i int) { p.i = i } +func (p *myType) Set(i int) { p.i = i } </pre> <p> Now any function which takes `myInterface` as a parameter will accept a variable of type `*myType`. +</p> <pre> -func getAndSet(x myInterface) {} +func GetAndSet(x myInterface) {} func f1() { var p myType - getAndSet(&p) + GetAndSet(&p) } </pre> <p> In other words, if we view `myInterface` as a C++ pure abstract base -class, defining `set` and `get` for +class, defining `Set` and `Get` for `*myType` made `*myType` automatically inherit from `myInterface`. A type may satisfy multiple interfaces. +</p> <p> An anonymous field may be used to implement something much like a C++ child class. +</p> <pre> type myChildType struct { myType; j int } -func (p *myChildType) get() int { p.j++; return p.myType.get() } +func (p *myChildType) Get() int { p.j++; return p.myType.Get() } </pre> <p> This effectively implements `myChildType` as a child of `myType`. +</p> <pre> func f2() { var p myChildType - getAndSet(&p) + GetAndSet(&p) } </pre> @@ -544,8 +619,9 @@ methods associated with the anonymous field are promoted to become methods of the enclosing type. In this case, because `myChildType` has an anonymous field of type `myType`, the methods of `myType` also become methods of `myChildType`. -In this example, the `get` method was -overridden, and the `set` method was inherited. +In this example, the `Get` method was +overridden, and the `Set` method was inherited. +</p> <p> This is not precisely the same as a child class in C++. @@ -553,21 +629,23 @@ When a method of an anonymous field is called, its receiver is the field, not the surrounding struct. In other words, methods on anonymous fields are not virtual functions. When you want the equivalent of a virtual function, use an interface. +</p> <p> -A variable which has an interface type may be converted to have a +A variable that has an interface type may be converted to have a different interface type using a special construct called a type assertion. This is implemented dynamically at run time, like C++ `dynamic_cast`. Unlike `dynamic_cast`, there does not need to be any declared relationship between the two interfaces. +</p> <pre> type myPrintInterface interface { - print() + Print() } func f3(x myInterface) { - x.(myPrintInterface).print() // type assertion to myPrintInterface + x.(myPrintInterface).Print() // type assertion to myPrintInterface } </pre> @@ -576,11 +654,13 @@ The conversion to `myPrintInterface` is entirely dynamic. It will work as long as the underlying type of x (the <em>dynamic type</em>) defines a `print` method. +</p> <p> Because the conversion is dynamic, it may be used to implement generic programming similar to templates in C++. This is done by manipulating values of the minimal interface. +</p> <pre> type Any interface { } @@ -593,16 +673,24 @@ values of the contained type. As the typing is dynamic rather than static, there is no equivalent of the way that a C++ template may inline the relevant operations. The operations are fully type-checked at run time, but all operations will involve a function call. +</p> <pre> -type iterator interface { - get() Any - set(v Any) - increment() - equal(arg *iterator) bool +type Iterator interface { + Get() Any + Set(v Any) + Increment() + Equal(arg Iterator) bool } </pre> +<p> +Note that `Equal` has an argument of +type `Iterator`. This does not behave like a C++ +template. See <a href="go_faq.html#t_and_equal_interface">the +FAQ</a>. +</p> + <h2 id="Goroutines">Goroutines</h2> <p>
- 変更点:
get
/set
メソッドがGet
/Set
に、myInterface
がmyPrintInterface
に、iterator
がIterator
にそれぞれ変更されました。関連する呼び出し箇所も全て更新されています。 - 意図: Goの命名規則(エクスポートされる識別子は先頭が大文字)に準拠するため。これにより、ドキュメントのコード例がGoのベストプラクティスに沿ったものになります。
- 変更点:
-
標準ライブラリの更新と短い変数宣言の利用:
--- a/doc/go_for_cpp_programmers.html +++ b/doc/go_for_cpp_programmers.html @@ -611,18 +699,20 @@ using the `go` statement. The `go` statement runs a function in a different, newly created, goroutine. All goroutines in a single program share the same address space. +</p> <p> Internally, goroutines act like coroutines that are multiplexed among multiple operating system threads. You do not have to worry about these details. +</p> <pre> func server(i int) { - for { - print(i) - sys.sleep(10) - }\n+\tfor { +\t\tfmt.Print(i) +\t\ttime.Sleep(10 * time.Second) +\t}\n } go server(1) go server(2) @@ -631,13 +721,16 @@ go server(2) <p> (Note that the `for` statement in the `server` function is equivalent to a C++ `while (true)` loop.) +</p> <p> Goroutines are (intended to be) cheap. +</p> <p> Function literals (which Go implements as closures) can be useful with the `go` statement. +</p> <pre> var g int @@ -658,21 +751,23 @@ operator. To receive a value on a channel, use `<-` as a unary operator. When calling functions, channels are passed by reference. +</p> <p> The Go library provides mutexes, but you can also use a single goroutine with a shared channel. Here is an example of using a manager function to control access to a single value. +</p> <pre> -type cmd struct { get bool; val int } -func manager(ch chan cmd) { - var val int = 0 +type Cmd struct { Get bool; Val int } +func Manager(ch chan Cmd) { + val := 0 for { - c := <- ch - if c.get { c.val = val; ch <- c } - else { val = c.val } + c := <-ch + if c.Get { c.Val = val; ch <- c } + else { val = c.Val } } } </pre> @@ -684,26 +779,28 @@ with the manager at once: a goroutine waiting for a response from the manager might receive a request from another goroutine instead. A solution is to pass in a channel. +</p> <pre> -type cmd2 struct { get bool; val int; ch <- chan int } -func manager2(ch chan cmd2) { - var val int = 0 +type Cmd2 struct { Get bool; Val int; Ch <- chan int } +func Manager2(ch chan Cmd2) { + val := 0 for { - c := <- ch - if c.get { c.ch <- val } - else { val = c.val } + c := <-ch + if c.Get { c.ch <- val } + else { val = c.Val } } } </pre> <p> -To use `manager2`, given a channel to it: +To use `Manager2`, given a channel to it: +</p> <pre> -func f4(ch <- chan cmd2) int { +func f4(ch <- chan Cmd2) int { myCh := make(chan int) - c := cmd2{ true, 0, myCh } // Composite literal syntax. + c := Cmd2{ true, 0, myCh } // Composite literal syntax. ch <- c return <-myCh }
- 変更点:
server
関数内でsys.sleep(10)
がtime.Sleep(10 * time.Second)
に、print(i)
がfmt.Print(i)
に変更されました。また、cmd
/manager
、cmd2
/manager2
の構造体名と関数名がCmd
/Manager
、Cmd2
/Manager2
に大文字化され、var val = 0
がval := 0
という短い変数宣言に置き換えられました。構造体フィールド名もget
/val
からGet
/Val
に変更されています。 - 意図: Goの標準ライブラリの現代的な利用法を反映し、より慣用的なコードスタイル(短い変数宣言、エクスポートされる識別子の大文字化)を示すため。
- 変更点:
これらの変更は、Go言語のドキュメントが常に最新のベストプラクティスと慣用句を反映するようにするための継続的な努力の一部です。
コアとなるコードの解説
このコミットのコアとなる変更は、doc/go_for_cpp_programmers.html
という単一のドキュメントファイルに対するものです。このファイルは、C++プログラマーがGo言語を学ぶ際に役立つように、両言語の概念的な違いや構文の違いを説明しています。
変更の主な目的は、「現在のGo言語の慣用的な記述(current Go)」にドキュメントを合わせることです。これは、Go言語が成熟するにつれて確立されたベストプラクティスや、より推奨されるAPIの使用法を反映させることを意味します。
具体的に、主要な変更点とその解説は以下の通りです。
-
命名規則の徹底 (大文字化):
- 変更前:
get()
,set()
,myInterface
,iterator
,cmd
,manager
など、小文字で始まる識別子が多数使われていました。 - 変更後: これらがそれぞれ
Get()
,Set()
,myPrintInterface
(インターフェース名が変更されたため)、Iterator
,Cmd
,Manager
など、大文字で始まるように修正されました。構造体のフィールド名も同様にget
,val
からGet
,Val
に変更されています。 - 解説: Go言語では、識別子の先頭が大文字であるか小文字であるかによって、その識別子がパッケージ外にエクスポートされるかどうかが決まります。大文字で始まる識別子はエクスポートされ、小文字で始まる識別子はパッケージ内でのみ利用可能です。このドキュメントはGoの基本的な概念を説明するものであり、特にインターフェースの例では、メソッドがインターフェースを満たすためにエクスポートされている必要があります。したがって、Goの慣用的な命名規則に厳密に従うことで、読者が正しいプラクティスを学ぶことができます。
- 変更前:
-
メモリ管理の慣用的な記述 (
new
と複合リテラル):- 変更点:
new
関数がGoではC++ほど一般的ではないことを説明し、代わりに複合リテラル(例:&S{0}
)を使った構造体の初期化が推奨されることを示す新しいセクションが追加されました。new(S)
、var s S; return &s
、return &S{0}
の3つのパターンが同等であり、最後の複合リテラルが最も慣用的であると説明されています。 - 解説: C++プログラマーは
new
キーワードを頻繁に使用するため、Goのnew
も同様に捉えがちです。しかし、Goのnew
は単に型のゼロ値へのポインタを返すだけであり、C++のコンストラクタ呼び出しとは異なります。Goでは、変数のアドレスを明示的に取得しても、コンパイラのエスケープ解析によって必要に応じてヒープに割り当てられるため、スタックに割り当てられた変数のアドレスを返しても安全です。このため、構造体を初期化してそのポインタを得る際には、&MyStruct{field: value}
のような複合リテラルがより柔軟で一般的です。この変更は、Goのメモリモデルと慣用的な初期化方法をC++プログラマーに正しく伝えるための重要な改善です。
- 変更点:
-
標準ライブラリの現代的な利用:
- 変更前: ゴルーチンの例で
sys.sleep(10)
やprint(i)
といった古いAPIが使われていました。 - 変更後: これらがそれぞれ
time.Sleep(10 * time.Second)
とfmt.Print(i)
に修正されました。 - 解説: Go言語の初期には
sys
パッケージが存在しましたが、後にtime
やfmt
といったより特化された標準パッケージに置き換えられました。この変更は、Goの標準ライブラリの進化を反映し、読者が最新かつ推奨されるAPIを使用するように促すものです。
- 変更前: ゴルーチンの例で
-
構文の明確化と慣用句の強調:
- 変更点: 短い変数宣言
:=
の例が追加され、セミコロンの自動挿入ルールに関する詳細な説明(特にC++プログラマーが陥りやすい間違いの例)が追加されました。また、スライス構文a[i:j]
の詳細な挙動(i
やj
の省略時の意味)が明確化されました。 - 解説: Goの構文はC++に似ている部分もありますが、微妙な違いが多数存在します。特に、
:=
はGoで非常に頻繁に使われる慣用句であり、セミコロンの自動挿入はC++の経験があるプログラマーにとっては混乱の元となることがあります。これらの変更は、Goの構文の特性をより正確に伝え、読者がGoらしいコードを書けるようにするためのものです。
- 変更点: 短い変数宣言
これらの変更は、単なる表面的な修正ではなく、Go言語の設計思想、慣用的なプログラミングスタイル、そして進化する標準ライブラリを反映した、ドキュメントの品質を向上させるための重要な更新です。
関連リンク
- Go言語公式ドキュメント: https://go.dev/doc/
- Go Tour: https://tour.golang.org/
- Effective Go: https://go.dev/doc/effective_go
- Go言語仕様: https://go.dev/ref/spec
- Go FAQ (特に "Why does my method call on a value of type T have a pointer receiver?" など、インターフェースとポインタに関するFAQ): https://go.dev/doc/faq
参考にした情報源リンク
- コミットのGitHubページ: https://github.com/golang/go/commit/b69fa69a8bf53fb4714d96b4daf80fd6de597111
- Go言語の命名規則に関する公式ドキュメントやスタイルガイド(例: Effective Goの"Names"セクション)
- Go言語における
new
とmake
、複合リテラルに関する解説記事(Goの公式ブログや信頼できる技術ブログなど) - Go言語のガベージコレクションに関する一般的な情報
- Go言語の歴史とAPIの変遷に関する情報(
sys
パッケージからtime
やfmt
への移行など) - Go言語のセミコロン自動挿入ルールに関する詳細な解説