2022年05月26日に初出の投稿

Last modified: 2022-05-27

Pro Go: The Complete Guide to Programming Reliable and Efficient Software Using Golang

今日は休憩時や仕事の合間や仕事が終わってからも "Pro Go: The Complete Guide to Programming Reliable and Efficient Software Using Golang" (Adam Freeman, Apress, 2022) を読んでいて、結局は1日で60ページほど読み進めた。最初に読んだ "Introducing Go: Build Reliable, Scalable Programs" (Caleb Doxsey, O'Reilly Media, 2016) よりも更に読みやすい英語で書かれているのは驚きだ。繰り返すが、僕は本書の冒頭の2章分は読む必要がないと思うので、そのあたりは飛ばして眺めただけだから、実質は第3章と第4章だけ(87ページまで)を読んだことになる。

さて、第3章はコマンドラインの go を解説していて、第4章はデータ型や定数、変数、そしてポインタの解説になっている。恐らく最大のポイントは、その名の通りポインタであろう。ただ、ポインタの有用性という説明は、簡潔に書かれてはいるけれど、もう少し丁寧に説明した方がいいと思った。よくプログラミング言語の解説でポインタは難所と言われている。しかし、ポインタが「難所」と言われている理由や事情は、たぶん大多数の人々が気楽に言っているようなことではない。つまり、ポインタが「難所」であるとしたら、それは学習する方にとって〈理解し難い〉からではなく、著者にとって〈説明し難い〉からだ。

ポインタが何であるかを定義することはたやすい。或る変数が格納されているメモリのアドレスを値にしている別の変数というだけのことだからだ。a という変数が整数の10を値としてもつように定義された場合、コンピュータでは値をメモリに格納する。そして、メモリのどこに値を格納するかは処理系の制御だけでなく OS の制約も受ける(この制約に違反することを「セグメンテーション違反」と呼んでいて、もっとも有名なのは OS の管理しているメモリ領域へアプリケーションが間違ってアクセスしようとしてしまう「一般保護違反(GPF)」だ)。そして、値を格納するアドレス(番地)を決めたら、そのアドレスを使って格納されている値にアクセスできるというわけだ。ここまでは大抵のテキストが解説するし、プログラミングの実務経験が殆どないに等しいペーパードライバー同然の学者や嘘つきライターや馬鹿でも書ける。

でも、これの何が有益なのかをクリアに書ける人間が少ないせいで、多くの初心者から見て無駄にややこしいことをしているだけに見えるのだ。例えば、いま話題にしている Go のテキストに出ているコードの例として、main() 関数のスコープに書くコードだけを取り上げると、

var first int32 = 24

var second *int32 = &first

fmt.Println( first, second )

というものがある。もちろん、first は 24 というリテラルが出力され、second は first の値が格納されているメモリのアドレスが出力される(たとえば "0xc000012088")。そして、second を使って first の値へアクセスしたいなら、*second という参照を使うと良いわけである。このような表記は、C 言語でも PHP でも大抵のポインタを使えるプログラミング言語なら大同小異だろう。

しかし、ここで多くの初心者が感じるのは、おそらく「アドレスという値が何の役に立つのか」という質問と、「first という元の値を制御することが目的なら、どうして first だけを使わないのか。second なんていらねーだろ」という疑問である。そして、僕が見る限りでは、これらの疑問に明解な説明をしている本が圧倒的に少ない。なぜなら、ポインタの定義が理解できている限り、ポインタを使っても使わなくても開発では問題がないからである。まさに二つめの疑問が示しているように、安っぽい定義だけでポインタを理解している人々が書く説明というのは、結局のところポインタを使おうが使うまいが変わらないようなレベルでしかシステム開発をしていないせいで、ポインタを使う意義をそもそも自分自身で納得していなくてもいいようになってしまっているからなのだ。つまりは、ポインタの定義だけで意義に納得できなくても使えてしまえる(そして使っても使わなくても大差ない)ゆえに、納得できなくて使うという人を増やしているだけのことなのだ。そして、そういう人たちも納得しないままに使って後輩に定義と、それから使おうが使うまいが大差ない実務を伝えることしかできないため、それこそ未熟なままポインタを使うプログラマと同じことを、ポインタの説明においても繰り返しているだけに終わるのだ。

ポインタ、つまり変数のメモリ・アドレスを扱う利点を一つ挙げるなら、それはメモリ・アドレスが変数の格納場所を示していることにある。アドレスが同じままであれば、そこに格納される値が変わっても、同じアドレスにある値へアクセスし続けられる。つまり、変数の値を入れる箱が同じ場所にあるなら、中身が変わっても〈その場所にある箱の(その特定のメモリ・アドレスに格納されている)〉中身を取り出すという動作は同じままにしておけるということだ。

これを再びコードで示すと、

first := []int{ 23, 87, 12 }

var second *int = &first[1]

fmt.Println( *second )

sort.Ints( first )

fmt.Println( *second )

のようになる。

1回目の Println() ではスライスの first[1] のメモリ・アドレスに格納されている87というリテラルが表示されるが、2回目の Println() ではスライス first を昇順でソートした結果、スライスの first[1] のメモリ・アドレスに格納されるリテラルが23に変更されたため、23と出力される。つまり、first[1] に紐づけて特定のメモリ・アドレスに格納されている値と、*second で参照される値とが〈同じ〉であることがポインタの利便性の要点(ポイント)なのではなく、そのメモリ・アドレスに格納されている値が変わろうと *second でその格納領域にアクセスし続けられることが要点なのだ。どうしても、最初は特定のメモリ・アドレスに格納される値と参照された値とが同じである状況から説明が始まってしまうので、同じ値を指しているという事に固執して理解してしまいがちだが、実際にはそこはポイントではないのだ。

しかし、これでも説明としては不十分かもしれない。なぜなら、結局はメモリのアドレスというものは、動かしているプログラムのたいていの目的からすれば二義的なことであり、本来は並び替えられようといまいと first[1] の値を参照する、つまり配列やスライスで1というインデックスがついた値を使うことが多くのプログラムの目的なのだから、ポインタなんて使わなくても straightforwardly に first[1] を使えばいいだけではないかというわけである。もちろんそうだ。それゆえ、実はポインタなんて殆どのウェブ・アプリケーションでは使われていないのである。わざわざメモリのアドレスを直に制御するという精密な事情でもない限り、僕もポインタなんて使う理由は限られていると思う。そもそも、情報セキュリティの観点から言っても、扱い方の失敗でプロセスが異常な処理を始めるどころか、システム全体への影響すらありえるし、バグとは別の経路からの攻撃によって同じ状況へ陥るリスクもあるのだから、むやみに使うようなものではない。

よって、システム開発にとって実は重要でもなければ本質でも不可欠でもない(ポインタをサポートしないプログラミング言語はたくさんある)、それどころか安全性の観点からは使うべきでないとすら言えるポインタについて、知ってる知らないとか、わかってるわかってないなどとオタク的なマウンティングを繰り返したところで、プロの仕事としてのエンジニアリングにおいては全くの無駄である・・・ということまで含めて書けるような人材が、まだ日本の業界には少ないのではないか。日本でプログラミング言語の本を書いてる人の多くは、いまだに「ぼくちゃん、こんなことまで知ってるよーん」的なオタク競争の自称覇者だったり、あるいはシステム開発をしたことすらない丘教練の名物教師なんかが、いまだに大半の出版社から新しい言語が出てくるたびに判で押したような教科書を出してるのが実態だ。(誰だ、山田何某や林何某の悪口を言ってるのは!)

  1. もっと新しいノート <<
  2. >> もっと古いノート

冒頭に戻る


※ 以下の SNS 共有ボタンは JavaScript を使っておらず、ボタンを押すまでは SNS サイトと全く通信しません。

Twitter Facebook