List of Interface - text.Baldanders.info
tag:text.Baldanders.info,2020-02-13:/tags
2020-02-13T22:43:11+09:00
帰ってきた「しっぽのさきっちょ」
https://text.baldanders.info/images/avatar.jpg
https://text.baldanders.info/images/avatar.jpg
#shimanego より: 配列とスライスとソート
tag:text.Baldanders.info,2020-02-13:/remark/2020/02/array-slice-sort/
2020-02-13T13:43:11+00:00
2021-12-04T02:40:05+00:00
Slice 周りで「あれ?」と思ったらこのスライドのことも思い出してあげてください。
Spiegel
https://baldanders.info/profile/
<p>今月も <a href="https://shimane-go.connpass.com/event/165192/" title="Shimane.go#04 - connpass">Shimane.go#04</a> に参加して図々しくもまた喋ってきた。
以下にそのとき使ったスライドを公開しておく。</p>
<ul>
<li><a href="https://slide.baldanders.info/shimane-go-2020-02-13/">配列とスライスとソート | slide.Baldanders.info</a></li>
</ul>
<p>ターゲットとしては “<a href="https://go-tour-jp.appspot.com/">A Tour of Go</a>” をひととおり終わらせて「なんとなく」 <a href="https://go.dev/">Go</a> が分かってきたかなぁ,という感じの人。
さすが松江は「お膝元」なので Ruby 経験者は多いが <a href="https://go.dev/">Go</a> には馴染みのない人が多いようなので。</p>
<p>実はソートの速度とかベンチマークを取ってやろうかとも考えたのだが,いい感じのデータが作れず(ただのランダムなデータ列ならいくらでも作れるけど,多分そうじゃない),諦めた。
いい方法を考えたらそのうちブログ記事にするかも。</p>
<p>たぶん <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>で引っかかりやすいのは <code>interface</code> と <code>slice</code> だと思う。
“<code>interface</code> の <code>slice</code>” とか最凶ダッグである(笑) ちうわけで <code>slice</code> 周りで「あれ?」と思ったら<a href="https://slide.baldanders.info/shimane-go-2020-02-13/" title="配列とスライスとソート | slide.Baldanders.info">このスライド</a>のことも思い出してあげてください。</p>
<p>まぁでも,やっぱ座学は退屈だよねぇ。
プログラムは書いてナンボだし。
なんか面白い遊びを提示できればいいんだけど。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://text.baldanders.info/golang/array-and-slice/">配列と Slice</a></li>
<li><a href="https://text.baldanders.info/golang/sort/">ソートを使う</a></li>
<li><a href="https://text.baldanders.info/golang/comparability/">インスタンスの比較可能性</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
Go 1.14 リリース候補版
tag:text.Baldanders.info,2020-02-08:/release/2020/02/go1_14-rc/
2020-02-08T06:33:22+00:00
2021-12-04T02:40:05+00:00
この記事では個人的に気になった点をかいつまんで紹介する。
Spiegel
https://baldanders.info/profile/
<p><a href="https://go.dev/">Go</a> 1.14 のリリース候補版が出た。
これに伴ってリリースノートのドラフト版も更新されたようだ。</p>
<ul>
<li><a href="https://groups.google.com/forum/#!topic/golang-announce/mB1Mp9RlQw8">Go 1.14 Release Candidate 1 is released - Google group</a></li>
<li><a href="https://tip.golang.org/doc/go1.14">Go 1.14 Release Notes - The Go Programming Language</a></li>
</ul>
<p>更にブログでは以下の記事で 1.15 についても言及されている。</p>
<ul>
<li><a href="https://blog.golang.org/go1.15-proposals">Proposals for Go 1.15 - The Go Blog</a></li>
</ul>
<p>詳しくはそれぞれの記事を読んでもらうとして,この記事では個人的に気になった点をかいつまんで紹介する。</p>
<h2>Try 終了のお知らせ</h2>
<p>「<a href="https://text.baldanders.info/release/2019/06/next-steps-toward-go-2/" title="Go 1.13 と 1.14 (Go 2 へ向けて)">Go 1.13 と 1.14</a>」で紹介した <code>try()</code> 組み込み関数の導入は見送られたらしい。</p>
<figure lang="en">
<blockquote><q>Our attempt seven months ago at providing a better error handling mechanism, the <a href="https://golang.org/issue/32437">try proposal</a>, met good support but also strong opposition and we decided to abandon it. In its aftermath there were many follow-up proposals, but none of them seemed convincing enough, clearly superior to the try proposal, or less likely to cause similar controversy</q>.</blockquote>
<figcaption><div>via <q><a href="https://blog.golang.org/go1.15-proposals">Proposals for Go 1.15</a></q></div></figcaption>
</figure>
<p>というわけで,エラー・ハンドリング周りはこれ以上の仕様追加・変更は(1.x の間は)なさそうである。</p>
<h2>埋め込み Interface の改善</h2>
<p><a href="https://go.dev/">Go</a> 1.14 では埋め込み interface の仕様が一部変更になる。</p>
<figure lang="en">
<blockquote><q>Per the <a href="https://github.com/golang/proposal/blob/master/design/6977-overlapping-interfaces.md">overlapping interfaces proposal</a>, Go 1.14 now permits embedding of interfaces with overlapping method sets: methods from an embedded interface may have the same names and identical signatures as methods already present in the (embedding) interface. This solves problems that typically (but not exclusively) occur with diamond-shaped embedding graphs. Explicitly declared methods in an interface must remain unique, as before</q>.</blockquote>
<figcaption><div>via <q><a href="https://tip.golang.org/doc/go1.14">Go 1.14 Release Notes (draft)</a></q></div></figcaption>
</figure>
<p><a href="https://go.dev/">Go</a> では interface は振る舞いのみを定義する型だが,入れ子にすることができる。
こんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Person</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Name</span><span class="p">()</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Age</span><span class="p">()</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Employee</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Person</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Level</span><span class="p">()</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>この例では <code>Employee</code> interface 型に <code>Person</code> interface 型が埋め込まれている。
つまり <code>Employee</code> 型では <code>Name()</code>, <code>Age()</code>, <code>Level()</code>, <code>String()</code> 各メソッドを要求しているわけだ。</p>
<p>ここで <code>Person</code> 型に <code>String()</code> メソッドを付けることを考える。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Person</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Name</span><span class="p">()</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Age</span><span class="p">()</span> <span class="kt">int</span>
</span></span><span class="line hl"><span class="cl"> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>修正された <code>Person</code> 型を使って <code>Employee</code> 型を定義しようとしても <q lang="en"><code>duplicate method String</code></q> とコンパイルエラーになる。
Interface 型の間で定義するメソッドを調整すればいいのだが,他パッケージの interface 型を埋め込む場合は,そのパッケージの仕様変更の影響をモロに受けることになる。</p>
<p>更に</p>
<figure style='margin:0 auto;text-align:center;'><a href="./diamond.puml"><img src="./diamond.png" srcset="./diamond.png 946w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<p>のようなひし形構造になっている場合はより複雑になる。</p>
<p><a href="https://go.dev/">Go</a> 1.14 では(関数型の同一性も含めて)同じメソッドについては重複を許容する。
上述のコード例でもコンパイル・エラーにならないわけだ。</p>
<p>ただし</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">E1</span> <span class="kd">interface</span><span class="p">{</span> <span class="nf">M</span><span class="p">(</span><span class="nx">x</span> <span class="kt">int</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">E2</span> <span class="kd">interface</span><span class="p">{</span> <span class="nf">M</span><span class="p">(</span><span class="nx">x</span> <span class="kt">float32</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">I</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">E1</span>
</span></span><span class="line"><span class="cl"> <span class="nx">E2</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>では(メソッド <code>M()</code> の型が同一ではないので)相変わらずコンパイル・エラーになるようだ。</p>
<h2>Preemptive なスケジューリング</h2>
<p>これは <a href="https://shimane-go.connpass.com/" title="Shimane.go - connpass">Shimane.go</a> の Slack で教えてもらったのだが, <a href="https://go.dev/">Go</a> 1.14 では preemptive (非協調的) なスケジューリング実装になるようだ。</p>
<figure lang="en">
<blockquote><q>Goroutines are now asynchronously preemptible. As a result, loops without function calls no longer potentially deadlock the scheduler or significantly delay garbage collection</q>.</blockquote>
<figcaption><div>via <q><a href="https://tip.golang.org/doc/go1.14">Go 1.14 Release Notes (draft)</a></q></div></figcaption>
</figure>
<p>もともと <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>には,ランタイムによって並列処理の実装詳細を隠蔽することにより,コード記述としての平行処理に注力できるというメリットがあるが preemptive なスケジューリングによって強化されることになる。</p>
<p>ただし全てのプラットフォームで有効になるのではなく</p>
<ul>
<li><code>windows/arm</code></li>
<li><code>darwin/arm</code></li>
<li><code>js/wasm</code></li>
<li><code>plan9/*</code></li>
</ul>
<p>は例外となるらしい。
WebAssembly や Plan 9 が non-preemptive になるのは分かるが, ARM アーキテクチャって実装が難しいのか?</p>
<h2>モジュール対応モード</h2>
<p>モジュール対応モード(module-aware mode)も色々と機能追加されるようだ。
特に <code>vendor</code> ディレクトリとの組み合わせは色々とできそうだ。</p>
<figure lang="en">
<blockquote><q>When the main module contains a top-level <code>vendor</code> directory and its <code>go.mod</code> file specifies go 1.14 or higher, the go command now defaults to <code>-mod=vendor</code> for operations that accept that flag. A new value for that flag, <code>-mod=mod</code>, causes the go command to instead load modules from the module cache (as when no vendor directory is present)</q>.</blockquote>
<figcaption><div>via <q><a href="https://tip.golang.org/doc/go1.14">Go 1.14 Release Notes (draft)</a></q></div></figcaption>
</figure>
<p>この辺は <a href="https://go.dev/">Go</a> 1.14 正式版がリリースされてから試してみよう。</p>
<h2>Launched <a href="https://pkg.go.dev/">pkg.go.dev</a></h2>
<ul>
<li><a href="https://blog.golang.org/pkg.go.dev-2020">Next steps for pkg.go.dev - The Go Blog</a></li>
</ul>
<p><a href="https://pkg.go.dev/">pkg.go.dev</a> は従来の <a href="https://godoc.org/">godoc.org</a> から置き換えることができる。</p>
<figure lang="en">
<blockquote><q>Like <a href="https://godoc.org/">godoc.org</a>, pkg.go.dev serves Go documentation. However, it also understands modules and has information about past versions of a package!</q></blockquote>
<figcaption><div>via <q><a href="https://blog.golang.org/pkg.go.dev-2020">Next steps for pkg.go.dev</a></q></div></figcaption>
</figure>
<p>実際に2020年後半には <a href="https://godoc.org/">godoc.org</a> へのリクエストを <a href="https://pkg.go.dev/">pkg.go.dev</a> にリダイレクトする計画があるらしい。</p>
<figure lang="en">
<blockquote><q>To minimize confusion about which site to use, later this year we are planning to redirect traffic from <a href="https://godoc.org/">godoc.org</a> to the corresponding page on <a href="https://pkg.go.dev/">pkg.go.dev</a></q>.</blockquote>
<figcaption><div>via <q><a href="https://blog.golang.org/pkg.go.dev-2020">Next steps for pkg.go.dev</a></q></div></figcaption>
</figure>
<p>標準パッケージもサードパーティのパッケージも同等に扱えるので,今後は <a href="https://pkg.go.dev/">pkg.go.dev</a> を参照するのがいいかもしれない。</p>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/4873118468?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51pUKQajnaL._SL160_.jpg" width="125" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/4873118468?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">Go言語による並行処理</a></dt>
<dd>Katherine Cox-Buday (著), 山口 能迪 (翻訳)</dd>
<dd>オライリージャパン 2018-10-26</dd>
<dd>単行本(ソフトカバー)</dd>
<dd>4873118468 (ASIN), 9784873118468 (EAN), 4873118468 (ISBN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description"><a href="https://www.oreilly.co.jp/books/9784873118468/">Eブック版もある</a>。感想は<a href="https://text.baldanders.info/remark/2018/11/concurrency-in-go/">こちら</a>。 Go 言語で並行処理を書くならこの本は必読書になるだろう。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2020-01-13">2020-01-13</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- Go言語による並行処理 -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/4908686033?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/51RKK5+6bpL._SL160_.jpg" width="112" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/4908686033?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">Goならわかるシステムプログラミング</a></dt>
<dd>渋川 よしき (著), ごっちん (イラスト)</dd>
<dd>ラムダノート 2017-10-23</dd>
<dd>単行本(ソフトカバー)</dd>
<dd>4908686033 (ASIN), 9784908686030 (EAN), 4908686033 (ISBN)</dd>
<dd>評価<abbr class="rating fa-sm" title="4"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="far fa-star"></i></abbr></dd>
</dl>
<p class="description"><a href="https://www.lambdanote.com/collections/go/products/go-ebook">PDF 版</a>あり。ファイルやソケットなどに代表される順次アクセスの汎化である io.Reader / io.Writer およびその派生・特化クラス,またプロセスやスレッドに関する解説が秀逸だと思う。さらに Docker コアの libcontainer についても解説がある。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2018-10-19">2018-10-19</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- Goならわかるシステムプログラミング -->
インスタンスの比較可能性
tag:text.Baldanders.info,2020-02-02:/golang/comparability/
2020-02-02T08:52:58+00:00
2021-12-04T02:40:05+00:00
少なくとも == および != 演算子が使えることを「比較可能」であると言う。
Spiegel
https://baldanders.info/profile/
<p><a href="https://shimane-go.connpass.com/event/165192/" title="Shimane.go#04 - connpass">次のイベント</a>に向けてネタの整理をしているところだが,その中でインスタンスの<ruby><rb>比較可能性</rb><rp> (</rp><rt>comparability</rt><rp>) </rp></ruby>についてきちんと整理したほうがよさそうな気がしたので,小ネタ記事として公開する。</p>
<h2>用語の定義(暫定)</h2>
<p>本題に入る前に,以下の2つの単語を,この記事限りの用語として定義する。
他所で使わないように(笑)</p>
<table>
<thead>
<tr>
<th style="text-align:center">用語</th>
<th>意味</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center">等値</td>
<td>インスタンスの値が等しい(equal)こと</td>
</tr>
<tr>
<td style="text-align:center">等価</td>
<td>インスタンスの型が同一(identical)であること</td>
</tr>
</tbody>
</table>
<p>プログラミングの世界で等値と等価に関して議論があることは知っているが,今回はまるっと無視する。
だって鬱陶しいんだもん。</p>
<h2>比較演算子</h2>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>ではインスタンス同士の比較演算子として</p>
<table>
<thead>
<tr>
<th style="text-align:center">演算子</th>
<th>名称</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:center"><code>==</code></td>
<td>equal</td>
</tr>
<tr>
<td style="text-align:center"><code>!=</code></td>
<td>not equal</td>
</tr>
<tr>
<td style="text-align:center"><code><</code></td>
<td>less</td>
</tr>
<tr>
<td style="text-align:center"><code><=</code></td>
<td>less or equal</td>
</tr>
<tr>
<td style="text-align:center"><code>></code></td>
<td>greater</td>
</tr>
<tr>
<td style="text-align:center"><code>>=</code></td>
<td>greater or equal</td>
</tr>
</tbody>
</table>
<p>の5つが定義されている。
このうち少なくとも <code>==</code> および <code>!=</code> が使えることを「比較可能(comparable)」であると言う。</p>
<h2>型の比較可能性</h2>
<p>インスタンス同士が比較可能であるためには以下の2つの条件がが必要である。</p>
<ol>
<li>インスタンスの型が同一(等価)であること</li>
<li>インスタンスの型が比較可能であること</li>
</ol>
<p>たとえばある型を別の型に再定義しただけの場合でも等価とは見なされず,コンパイルエラーになる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Number</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">c1</span> <span class="kt">int</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">c2</span> <span class="nx">Number</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">c1</span> <span class="o">==</span> <span class="nx">c2</span><span class="p">)</span> <span class="c1">//compile error: mismatched types int and Number
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>ただし,等価な型にキャスト可能であれば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Number</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">c1</span> <span class="kt">int</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">c2</span> <span class="nx">Number</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">c1</span> <span class="o">==</span> <span class="nb">int</span><span class="p">(</span><span class="nx">c2</span><span class="p">))</span> <span class="c1">//true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>などとできる。
また type alias であれば等価とみなされる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl"><span class="kd">type</span> <span class="nx">Number</span> <span class="p">=</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">c1</span> <span class="kt">int</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">c2</span> <span class="nx">Number</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">c1</span> <span class="o">==</span> <span class="nx">c2</span><span class="p">)</span> <span class="c1">//true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>型の比較可能性については以下の通り。</p>
<table>
<thead>
<tr>
<th style="text-align:right">型</th>
<th style="text-align:center">等値比較</th>
<th style="text-align:center">大小比較</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align:right">整数型</td>
<td style="text-align:center">可</td>
<td style="text-align:center">可</td>
</tr>
<tr>
<td style="text-align:right">浮動小数点数型</td>
<td style="text-align:center">可</td>
<td style="text-align:center">可</td>
</tr>
<tr>
<td style="text-align:right">複素数型</td>
<td style="text-align:center">可</td>
<td style="text-align:center">不可</td>
</tr>
<tr>
<td style="text-align:right">真偽型</td>
<td style="text-align:center">可</td>
<td style="text-align:center">不可</td>
</tr>
<tr>
<td style="text-align:right">構造体</td>
<td style="text-align:center">可</td>
<td style="text-align:center">不可</td>
</tr>
<tr>
<td style="text-align:right">配列</td>
<td style="text-align:center">可</td>
<td style="text-align:center">不可</td>
</tr>
<tr>
<td style="text-align:right">文字列</td>
<td style="text-align:center">可</td>
<td style="text-align:center">可</td>
</tr>
<tr>
<td style="text-align:right">Slice 型</td>
<td style="text-align:center">不可</td>
<td style="text-align:center">不可</td>
</tr>
<tr>
<td style="text-align:right">Map 型</td>
<td style="text-align:center">不可</td>
<td style="text-align:center">不可</td>
</tr>
<tr>
<td style="text-align:right">関数型</td>
<td style="text-align:center">不可</td>
<td style="text-align:center">不可</td>
</tr>
<tr>
<td style="text-align:right">Channel 型</td>
<td style="text-align:center">可</td>
<td style="text-align:center">不可</td>
</tr>
<tr>
<td style="text-align:right">Interface 型</td>
<td style="text-align:center">可</td>
<td style="text-align:center">不可</td>
</tr>
<tr>
<td style="text-align:right">ポインタ</td>
<td style="text-align:center">可</td>
<td style="text-align:center">不可</td>
</tr>
</tbody>
</table>
<p>以下,補足。</p>
<h3>NaN は比較可能だが比較できない</h3>
<p>NaN (Not a Number) は浮動小数点数型における(ゼロ除算などの)特別な状態を示す。
NaN 自体は比較可能なのだが,常に同じ結果を返すので,比較演算子は使えない。
浮動小数点数の値が NaN かどうか調べるには <a href="https://golang.org/pkg/math/" title="math - The Go Programming Language"><code>math</code></a><code>.IsNaN()</code> 関数を使う。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"math"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">z</span> <span class="kt">float64</span>
</span></span><span class="line"><span class="cl"> <span class="nx">nan</span> <span class="o">:=</span> <span class="nx">z</span> <span class="o">/</span> <span class="nx">z</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">nan</span><span class="p">)</span> <span class="c1">//NaN
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"NaN == NaN ->"</span><span class="p">,</span> <span class="nx">nan</span> <span class="o">==</span> <span class="nx">math</span><span class="p">.</span><span class="nf">NaN</span><span class="p">())</span> <span class="c1">//false
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"NaN != NaN ->"</span><span class="p">,</span> <span class="nx">nan</span> <span class="o">!=</span> <span class="nx">math</span><span class="p">.</span><span class="nf">NaN</span><span class="p">())</span> <span class="c1">//true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"NaN > NaN ->"</span><span class="p">,</span> <span class="nx">nan</span> <span class="p">></span> <span class="nx">math</span><span class="p">.</span><span class="nf">NaN</span><span class="p">())</span> <span class="c1">//false
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"NaN < NaN ->"</span><span class="p">,</span> <span class="nx">nan</span> <span class="p"><</span> <span class="nx">math</span><span class="p">.</span><span class="nf">NaN</span><span class="p">())</span> <span class="c1">//false
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"math.IsNaN(NaN) ->"</span><span class="p">,</span> <span class="nx">math</span><span class="p">.</span><span class="nf">IsNaN</span><span class="p">(</span><span class="nx">nan</span><span class="p">))</span> <span class="c1">//true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h3>構造体要素の型</h3>
<p>構造体は,要素の型が全て比較可能であれば,比較可能である。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Number</span> <span class="kd">struct</span><span class="p">{</span> <span class="nx">num</span> <span class="kt">int</span> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c1</span> <span class="o">:=</span> <span class="nx">Number</span><span class="p">{</span><span class="nx">num</span><span class="p">:</span> <span class="mi">1</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c2</span> <span class="o">:=</span> <span class="nx">Number</span><span class="p">{</span><span class="nx">num</span><span class="p">:</span> <span class="mi">1</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">c1</span> <span class="o">==</span> <span class="nx">c2</span><span class="p">)</span> <span class="c1">//true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h3>配列要素の型</h3>
<p>配列は,要素の型が比較可能であれば,比較可能である。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b1</span> <span class="o">:=</span> <span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="kt">byte</span><span class="p">{</span><span class="mi">1</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b2</span> <span class="o">:=</span> <span class="nx">b1</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">b1</span> <span class="o">==</span> <span class="nx">b2</span><span class="p">)</span> <span class="c1">//true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"> <span class="nx">b3</span> <span class="o">:=</span> <span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="kt">byte</span><span class="p">{</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">b1</span> <span class="o">==</span> <span class="nx">b3</span><span class="p">)</span> <span class="c1">//compile error: mismatched types [1]byte and [2]byte
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>ちなみに上のコードの <code>[1]byte</code> と <code>[2]byte</code> は等価ではないのでご注意を。
配列と Slice の関係については拙文「<a href="https://text.baldanders.info/golang/array-and-slice/">配列と Slice</a>」を参考にどうぞ。</p>
<h3>ポインタの型</h3>
<p>ポインタはインスタンスのアドレッシングを指すものだが,等価なインスタンスのポインタであれば比較可能である。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Number</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">c1</span> <span class="kt">int</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="o">&</span><span class="nx">c1</span> <span class="o">==</span> <span class="o">&</span><span class="nx">c1</span><span class="p">)</span> <span class="c1">//true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">c2</span> <span class="nx">Number</span> <span class="p">=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="o">&</span><span class="nx">c1</span> <span class="o">==</span> <span class="o">&</span><span class="nx">c2</span><span class="p">)</span> <span class="c1">//compile error: mismatched types *int and *Number
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>たとえば Slice 型や Map 型は比較可能ではないがポインタは比較できる(内容を比較しているわけではない)。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c1</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c2</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">c1</span> <span class="o">==</span> <span class="nx">c2</span><span class="p">)</span> <span class="c1">//compile error: slice can only be compared to nil
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="o">&</span><span class="nx">c1</span> <span class="o">==</span> <span class="o">&</span><span class="nx">c2</span><span class="p">)</span> <span class="c1">//false
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h3>nil と比較可能な型</h3>
<p><code>nil</code> は(null 参照など)ポインタ値の特別な状態を示す。
なので <code>nil</code> はポインタと比較可能である。
他に <code>nil</code> と比較可能な型は以下の通り。</p>
<ul>
<li>Slice 型</li>
<li>Map 型</li>
<li>関数型</li>
<li>Channel 型</li>
<li>Interface 型</li>
</ul>
<p>Slice 型, Map 型, 関数型は比較可能ではないが <code>nil</code> とは比較可能である。</p>
<h2>Interface 型の比較可能性</h2>
<p>Interface 型は型情報と値への参照を属性として持っている。
Interface 型が参照している型を動的な型(dynamic type),参照値を動的な値(dynamic value)と呼ぶ。
動的な型も値も実行時に決まるからだ。</p>
<p>たとえば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Binary</span> <span class="kt">uint64</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">i</span> <span class="nx">Binary</span><span class="p">)</span> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">FormatUint</span><span class="p">(</span><span class="nb">uint64</span><span class="p">(</span><span class="nx">i</span><span class="p">),</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>という型の定義に対して <a href="https://golang.org/pkg/fmt/" title="fmt - The Go Programming Language"><code>fmt</code></a><code>.Stringer</code> インタフェースを適用してみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="o">:=</span> <span class="nf">Binary</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Stringer</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">s</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 11001000
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>このときの <a href="https://golang.org/pkg/fmt/" title="fmt - The Go Programming Language"><code>fmt</code></a><code>.Stringer</code> インタフェースは以下のような構造になっている。</p>
<figure lang="en">
<blockquote><figure style='margin:0 auto;text-align:center;'><a href="https://research.swtch.com/interfaces"><img src="https://research.swtch.com/gointer2.png" srcset="https://research.swtch.com/gointer2.png 500w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
</blockquote>
<figcaption><div>via <q><a href="https://research.swtch.com/interfaces">Go Data Structures: Interfaces</a></q></div></figcaption>
</figure>
<p>Interface 型は,動的な型が比較可能であれば,比較可能である。</p>
<h3>Interface 型の比較</h3>
<p>Interface 型インスタンスの動的な型が等価で比較可能あれば値の等値性を調べられる。
更に Interface 型インスタンスの動的な型と等価な型のインスタンスとも(比較可能な型であれば)比較できる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c1</span> <span class="o">:=</span> <span class="p">(</span><span class="kd">interface</span><span class="p">{})(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c2</span> <span class="o">:=</span> <span class="p">(</span><span class="kd">interface</span><span class="p">{})(</span><span class="mi">1</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c3</span> <span class="o">:=</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">c1</span> <span class="o">==</span> <span class="nx">c2</span><span class="p">)</span> <span class="c1">//true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">c1</span> <span class="o">==</span> <span class="nx">c3</span><span class="p">)</span> <span class="c1">//true
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>通常の型では等価でない型同士の比較はコンパイルエラーになるが Interface 型同士であれば単に <code>false</code> なるだけである。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c1</span> <span class="o">:=</span> <span class="p">(</span><span class="kd">interface</span><span class="p">{})(</span><span class="nb">int</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c2</span> <span class="o">:=</span> <span class="p">(</span><span class="kd">interface</span><span class="p">{})(</span><span class="nb">byte</span><span class="p">(</span><span class="mi">1</span><span class="p">))</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c3</span> <span class="o">:=</span> <span class="p">(</span><span class="kd">interface</span><span class="p">{})([]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">c1</span> <span class="o">==</span> <span class="nx">c2</span><span class="p">)</span> <span class="c1">//false
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">c1</span> <span class="o">==</span> <span class="nx">c3</span><span class="p">)</span> <span class="c1">//false
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>ただし動的な型が等価でも比較可能ではない場合には(コンパイルは通るが)実行時 panic になる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c1</span> <span class="o">:=</span> <span class="p">(</span><span class="kd">interface</span><span class="p">{})([]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c2</span> <span class="o">:=</span> <span class="p">(</span><span class="kd">interface</span><span class="p">{})([]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">c1</span> <span class="o">==</span> <span class="nx">c2</span><span class="p">)</span> <span class="c1">//panic: runtime error: comparing uncomparable type []int
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><h3>Interface 型インスタンスが比較可能か検証する</h3>
<p>比較結果が <code>false</code> になるのはまだしも,実行時 panic はいただけない。
Panic を回避するには比較する前に動的な型が比較可能かどうか調べる必要がある。</p>
<p>動的な型が比較可能かどうか調べるには <a href="https://golang.org/pkg/reflect/" title="reflect - The Go Programming Language"><code>reflect</code></a> パッケージが使える。
たとえば,こんな感じでどうだろう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"reflect"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Compare</span><span class="p">(</span><span class="nx">left</span><span class="p">,</span> <span class="nx">right</span> <span class="kd">interface</span><span class="p">{})</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">if</span> <span class="p">!</span><span class="nx">reflect</span><span class="p">.</span><span class="nf">TypeOf</span><span class="p">(</span><span class="nx">left</span><span class="p">).</span><span class="nf">Comparable</span><span class="p">()</span> <span class="o">&&</span> <span class="p">!</span><span class="nx">reflect</span><span class="p">.</span><span class="nf">TypeOf</span><span class="p">(</span><span class="nx">right</span><span class="p">).</span><span class="nf">Comparable</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">return</span> <span class="kc">false</span>
</span></span><span class="line hl"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">left</span> <span class="o">==</span> <span class="nx">right</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c1</span> <span class="o">:=</span> <span class="p">(</span><span class="kd">interface</span><span class="p">{})([]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c2</span> <span class="o">:=</span> <span class="p">(</span><span class="kd">interface</span><span class="p">{})([]</span><span class="kt">int</span><span class="p">{</span><span class="mi">1</span><span class="p">})</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nf">Compare</span><span class="p">(</span><span class="nx">c1</span><span class="p">,</span> <span class="nx">c2</span><span class="p">))</span> <span class="c1">//false
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>まぁ,実際に組み込むには(動的な型がポインタの際の処理など)もう少し工夫がいるだろう。
また,一般的に <a href="https://golang.org/pkg/reflect/" title="reflect - The Go Programming Language"><code>reflect</code></a> は遅いと言われてるので,他の手段も考える必要があるかもしれない。</p>
<p>たとえば標準の <a href="https://golang.org/pkg/errors/" title="errors - The Go Programming Language"><code>errors</code></a><code>.Is()</code> 関数は</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// Is reports whether any error in err's chain matches target.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">Is</span><span class="p">(</span><span class="nx">err</span><span class="p">,</span> <span class="nx">target</span> <span class="kt">error</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">target</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span> <span class="o">==</span> <span class="nx">target</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl"> <span class="nx">isComparable</span> <span class="o">:=</span> <span class="nx">reflectlite</span><span class="p">.</span><span class="nf">TypeOf</span><span class="p">(</span><span class="nx">target</span><span class="p">).</span><span class="nf">Comparable</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">isComparable</span> <span class="o">&&</span> <span class="nx">err</span> <span class="o">==</span> <span class="nx">target</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">err</span><span class="p">.(</span><span class="kd">interface</span><span class="p">{</span> <span class="nf">Is</span><span class="p">(</span><span class="kt">error</span><span class="p">)</span> <span class="kt">bool</span> <span class="p">});</span> <span class="nx">ok</span> <span class="o">&&</span> <span class="nx">x</span><span class="p">.</span><span class="nf">Is</span><span class="p">(</span><span class="nx">target</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">true</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// TODO: consider supporing target.Is(err). This would allow
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// user-definable predicates, but also may allow for coping with sloppy
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// APIs, thereby making it easier to get away with them.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">if</span> <span class="nx">err</span> <span class="p">=</span> <span class="nf">Unwrap</span><span class="p">(</span><span class="nx">err</span><span class="p">);</span> <span class="nx">err</span> <span class="o">==</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>となっていて,独自の internal package を使っているようだ。</p>
<h2>ブックマーク</h2>
<ul>
<li>
<p><a href="http://qiita.com/chimatter/items/b0879401d6666589ab71">Go で interface {} の中身がポインタならその参照先を取得する - Qiita</a></p>
</li>
<li>
<p><a href="https://budougumi0617.github.io/2019/07/07/prevent-runtime-error-by-pointer/">Sliceを含んだ構造体が等値演算子(==)でpanicを引き起こすのを回避する #golang - My External Storage</a></p>
</li>
<li>
<p><a href="https://text.baldanders.info/golang/nil-is-nil/">nil は nil</a></p>
</li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
nil は nil
tag:text.Baldanders.info,2019-08-18:/golang/nil-is-nil/
2019-08-17T23:46:56+00:00
2021-11-28T02:58:44+00:00
nil は状態を表す「識別子」あるいは「表現」に過ぎず,それ自身は型も値も持たない。
Spiegel
https://baldanders.info/profile/
<div class="box"><p><strong>【2020-10-15 追記】</strong>
最近 <a href="https://zenn.dev/" title="Zenn|プログラマーのための情報共有コミュニティ">Zenn</a> で以下の記事を書いたので,こちらも併せてどうぞ。</p>
<ul>
<li><a href="https://zenn.dev/spiegel/articles/20201010-ni-is-not-nil">nil == nil でないとき(または Go プログラマは息をするように依存を注入する)</a></li>
</ul>
<p>Interface 型の <code>nil</code> 値は「依存の注入(dependency injection)」と絡めて考えると分かりやすいかも知れない。
宣伝でした(笑)</p>
</div>
<p>Qiita を覗いてたら</p>
<ul>
<li><a href="https://qiita.com/momotaro98/items/ee2aea840017266e659d">Goのnilは(nil, nil)という(型, 値)ペアのインターフェースだと把握すれば混乱しない - Qiita</a></li>
</ul>
<p>という記事を見かけた。
おそらくは <code>nil</code> の理解のための方便として意図的に書かれているのだろう。
それはそれで悪くないのだが,微妙に危険な香りがするので私なりの解説を記しておく。</p>
<h2>nil は nil</h2>
<p>たとえば <code>fmt.Printf()</code> 関数などで <code>nil</code> の型と値を取ろうとすると</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"Type: %T, Value: %v"</span><span class="p">,</span> <span class="kc">nil</span><span class="p">,</span> <span class="kc">nil</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="c1">// Output:
</span></span></span><span class="line"><span class="cl"><span class="c1">// Type: <nil>, Value: <nil>
</span></span></span></code></pre></div><p>などと表示されるので,いかにも <code>nil</code> 型のようなものがあるように見えるが,実際にはこれは「型がない」ことを示している。
同様に値についても,厳密には <code>nil</code> という値ではなく「値がない」ことを示しているのだ。</p>
<p>「<code>nil</code> とは何か」をきちんと定義した文章は見かけないが, <a href="https://go.dev/ref/spec" title="The Go Programming Language Specification - The Go Programming Language">Go 言語の仕様書</a>には,型 <code>T</code> の変数 <code>x</code> に対して</p>
<figure lang="en">
<blockquote><q><code>x</code> is the predeclared identifier <code>nil</code> and <code>T</code> is a pointer, function, slice, map, channel, or interface type.</q></blockquote>
<figcaption><div>via <q><a href="https://go.dev/ref/spec">The Go Programming Language Specification</a></q></div></figcaption>
</figure>
<p>であると記されている。</p>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>では,ある型の値が「宣言されていない」状態のことを「ゼロ値(zero value)」と呼んでいる。
たとえば int などの数値型では「ゼロ値」として数値の <code>0</code> を, bool では false を,文字列では空文字列をとる。
同じようにポインタ型や interface 型などでは <code>nil</code> を「ゼロ値」としましょう,ということなのである。
このように仕様として定義することで曖昧な状態を排除でき,私達ユーザは安心してその変数を使用することができるわけだ。</p>
<p>したがって <code>nil</code> は状態を表す「識別子」あるいは「表現」に過ぎず<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>,それ自身は型も値も持たない<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。
強いて言うなら(プログラミング言語で最も悪名高いとされる<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>)「null 参照」の一種だとは言えるだろう。</p>
<p>だから本当は</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>なんかも</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">if</span> <span class="p">!(</span><span class="nx">err</span> <span class="nx">is</span> <span class="kc">nil</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="o">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>みたいな感じに書ければ分かりやすかったのかもしれないが,シンプルを旨とする <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>でそんな迂遠な表現がとられるわけもなく,敢えて「<code>nil</code> との同値性(equality)」という表現をとっているわけだ(偏見)。</p>
<h3>nil と比較可能な型</h3>
<p><code>nil</code> と <code>==</code> または <code>!=</code> で比較可能(comparable)な型は以下の通り(ポインタ型を除く)。</p>
<ul>
<li>slice 型</li>
<li>map 型</li>
<li>関数型</li>
<li>channel 型</li>
<li>interface 型</li>
</ul>
<p>このうち slice, map, および関数の各型は <code>nil</code> との同値性のみ検証できる(<code>nil</code> でないオブジェクト同士は単純比較できない<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>)。
また interface 型はクセが強い(笑)型なので,後述の通り,取り扱いには若干の注意が必要である。</p>
<h2>Interface 型は,型と値への参照を属性に持つ</h2>
<p>まずは,以下の簡単なコードを考えてみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl"> <span class="s">"strconv"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Binary</span> <span class="kt">uint64</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">i</span> <span class="nx">Binary</span><span class="p">)</span> <span class="nf">Get</span><span class="p">()</span> <span class="kt">uint64</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">uint64</span><span class="p">(</span><span class="nx">i</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">i</span> <span class="nx">Binary</span><span class="p">)</span> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">strconv</span><span class="p">.</span><span class="nf">FormatUint</span><span class="p">(</span><span class="nx">i</span><span class="p">.</span><span class="nf">Get</span><span class="p">(),</span> <span class="mi">2</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="o">:=</span> <span class="nf">Binary</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">b</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">// 11001000
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>変数 <code>b</code> を図式化してみよう<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>。
こんな感じ。</p>
<figure lang="en">
<blockquote class="nobox lightmode" style='margin:0 auto;text-align:center;'>
<a href="https://research.swtch.com/interfaces"><img src="https://research.swtch.com/gointer1.png" srcset="https://research.swtch.com/gointer1.png 500w" sizes="(min-width:600px) 500px, 80vw" alt="Go Data Structures: Interfaces" loading="lazy"></a>
</blockquote>
<figcaption><div>via <q><a href="https://research.swtch.com/interfaces">Go Data Structures: Interfaces</a></q></div></figcaption>
</figure>
<p>これを覚えておいてね。</p>
<p>ここで <code>main()</code> 関数の中身を以下のように変えてみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">b</span> <span class="o">:=</span> <span class="nf">Binary</span><span class="p">(</span><span class="mi">200</span><span class="p">)</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">s</span> <span class="o">:=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Stringer</span><span class="p">(</span><span class="nx">b</span><span class="p">)</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">s</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div>
<p>ちなみに <a href="https://pkg.go.dev/fmt" title="fmt package · pkg.go.dev"><code>fmt</code></a><code>.Stringer</code> 型は以下に定義される interface 型である。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Stringer</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>ゆえに変数 <code>s</code> は以下のように図式化できる。</p>
<figure lang="en">
<blockquote class="nobox lightmode" style='margin:0 auto;text-align:center;'>
<a href="https://research.swtch.com/interfaces"><img src="https://research.swtch.com/gointer2.png" srcset="https://research.swtch.com/gointer2.png 500w" sizes="(min-width:600px) 500px, 80vw" alt="Go Data Structures: Interfaces" loading="lazy"></a>
</blockquote>
<figcaption><div>via <q><a href="https://research.swtch.com/interfaces">Go Data Structures: Interfaces</a></q></div></figcaption>
</figure>
<p>このように interface 型は,型と値への参照を属性に持つオブジェクトとして実装されている。</p>
<div class="box"><p>ただし要素が空の <code>interface{}</code> 型では</p>
<figure lang="en">
<blockquote class="nobox lightmode" style='margin:0 auto;text-align:center;'>
<a href="https://research.swtch.com/interfaces"><img src="https://research.swtch.com/gointer3.png" srcset="https://research.swtch.com/gointer3.png 500w" sizes="(min-width:600px) 500px, 80vw" alt="Go Data Structures: Interfaces" loading="lazy"></a>
</blockquote>
<figcaption><div>via <q><a href="https://research.swtch.com/interfaces">Go Data Structures: Interfaces</a></q></div></figcaption>
</figure>
<p>のように最適化されているらしい。
まぁ,ユーザレベルで両者を区別する必要はないけれど。</p>
</div>
<p>interface 型では <code>nil</code> を「ゼロ値」とすると書いたが,そのためには「型と値」の2つの属性が共に <code>nil</code> でなければならない。
値(への参照)だけが <code>nil</code> でも型全体としては <code>nil</code> にならないのである。</p>
<p>これでハマりやすいのがエラーハンドリングである。</p>
<h2>エラーハンドリングのハマりどころ</h2>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の組み込み型である <code>error</code> は以下のように表すことができる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// The error built-in interface type is the conventional interface for
</span></span></span><span class="line"><span class="cl"><span class="c1">// representing an error condition, with the nil value representing no error.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="kt">error</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これを踏まえて,こんなコードを考えてみよう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">ErrorObject</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">ErrorObject</span><span class="p">)</span> <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">"I'm error object."</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="o">*</span><span class="nx">ErrorObject</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">bar</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">foo</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">bar</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"%#v is not nil\n"</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Printf</span><span class="p">(</span><span class="s">"%#v is nil\n"</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>このコードはコンパイルエラーにならないし完全に動くが,実行結果は</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">(*main.ErrorObject)(nil) is not nil
</span></span></code></pre></div><p>となる。
前節まで読んだなら既にお分かりだろうが <code>bar()</code> 関数の返り値の <code>error</code> は <code>*ErrorObject</code> という型を持つため <code>nil</code> にならないのである。
したがって <code>err != nil</code> は真(<code>true</code>)となる。</p>
<p><code>bar()</code> 関数の返り値を正しく評価するには <a href="https://go.dev/ref/spec#Conversions">Conversion</a> 構文で型を括りだすか,いっそ <code>foo()</code> 関数を</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">foo</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>と書き換えるかだろう。
まぁ,後者だよね。
そうすれば実行結果は</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl"><nil> is nil
</span></span></code></pre></div><p>となる。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://text.baldanders.info/golang/error-handling/">エラー・ハンドリングについて(追記あり)</a></li>
</ul>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>もちろんこれは言語仕様上の話で実装上は何らかの値をとる。「<a href="https://zenn.dev/dqneo/articles/436bb59d565be7">Go言語における式の評価文脈を理解する</a>」によると本当にゼロで埋めるらしい。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>「型も値も持たない」という意味では最初に紹介した記事の “Goのnilは(nil, nil)” は間違いではなと思う。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p>拙文「<a href="https://text.baldanders.info/remark/2016/11/null-safety/">「null 安全」について</a>」を参照のこと。 <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:4">
<p>slice 型と map 型については <a href="https://golang.org/pkg/fmt/" title="fmt - The Go Programming Language"><code>reflect</code></a><code>.DeepEqual()</code> 関数で <code>nil</code> 以外のオブジェクトと比較可能である。 <a href="#fnref:4" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:5">
<p>引用元の記事(“<a href="https://research.swtch.com/interfaces">Go Data Structures: Interfaces</a>”)では 1 word = 32 bits のシステムとして解説されているのでご注意を。 <a href="#fnref:5" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Go 言語で Factory Method Pattern を構成できるか
tag:text.Baldanders.info,2018-12-30:/golang/factory-method-pattern/
2018-12-30T06:24:48+00:00
2021-08-12T21:22:05+00:00
継承できないなら注入すればいいじゃない。
Spiegel
https://baldanders.info/profile/
<p>少し前に面白い記事を見かけたのだが</p>
<ul>
<li><a href="https://qiita.com/daiching/items/e986ccd8a3f18c56c406">Go言語で factory method パターンを書いてみる - Qiita</a></li>
</ul>
<p>「これって factory method pattern じゃないよね」と思いつつ「<a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>には「継承」がない」ことを説明する好例だと気付いたので今回の記事を書いてみた。</p>
<h2>Factory Method Pattern とは</h2>
<p>まずは factory method pattern の解説から。
以下にクラス図を示す。</p>
<figure style='margin:0 auto;text-align:center;'><a href="./factory-method-pattern.puml"><img src="./factory-method-pattern.png" srcset="./factory-method-pattern.png 1165w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<p><code>Product</code> は抽象クラスで <code>ConcreteProduct</code> が <code>Product</code> の実装クラスである。
<code>Creator</code> クラスの <code>factoryMethod()</code> メソッドは <code>Product</code> インスタンスを返す抽象メソッドで,サブクラスの <code>ConcreteCreator</code> で実装されている。</p>
<p><code>Creator</code> クラスを利用する側はサブクラスの <code>ConcreteCreator</code> を <code>Creator</code> クラスとして生成し <code>anOperation()</code> メソッドをキックする。</p>
<p>ポイントは <code>anOperation()</code> メソッドが実装メソッドで,内部では <code>factoryMethod()</code> メソッドが呼ばれている点である。
ただし <code>Creator</code> インスタンスの実体は <code>ConcreteCreator</code> クラスなので, <code>anOperation()</code> メソッドから呼ばれるのはオーバーライドされた <code>ConcreteCreator</code> のほうの <code>factoryMethod()</code> であることが期待される,というわけだ。</p>
<p>本来なら <code>Creator</code> インスタント生成時にどの <code>ConcreteProduct</code> インスタンスを(<code>Product</code> クラスとして)使うのか判断すればいいのだが(これが factory pattern),それだと <code>ConcreteProduct</code> クラスが増えるたびに <code>Creator</code> クラスも修正しなければならず管理が煩雑になる。</p>
<p>そこで <code>ConcreteProduct</code> クラスと一対一に対応する <code>ConcreteCreator</code> クラスをつくり,その中でどの <code>ConcreteProduct</code> インスタンスを(<code>Product</code> クラスとして)使うのか判断している,つまり「<a href="https://www.amazon.co.jp/exec/obidos/ASIN/B00I8ATHGW/baldandersinf-22" title="増補改訂版 Java言語で学ぶデザインパターン入門 | 結城 浩 | コンピュータ・IT | Kindleストア | Amazon">インスタンス作成をサブクラスにまかせる</a>」のである。</p>
<h2><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>には「継承(Inheritance)」がない</h2>
<p>しかし,このデザイン・パターンは <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>では使えない。
何故なら「<a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>には「継承」がない」からである。</p>
<h3>汎化と構造的部分型(Structural Subtyping)</h3>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>ではクラス間の「汎化」関係を実装する手段として interface 型を持っているが,これは「振る舞い」のみを定義する。
先程の <code>Creator</code> クラスのように抽象メソッドと実装メソッドをひとつのクラスの中に混ぜ込むことはできない。
Interface のような型を一般的には「構造的部分型」と呼ぶらしい<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。</p>
<h3>埋め込み(Embedding)と委譲(Delegation)</h3>
<p>もうひとつ <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>には型を構造体のフィールドとして埋め込む機能がある。
たとえば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">parent</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span></code></pre></div><p>という型があるとして</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">child</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">parent</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>とすれば <code>child</code> 型は <code>parent</code> 型の属性や操作をそのまま使うことができる。
こんな感じ。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">parent</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="nx">parent</span><span class="p">)</span> <span class="nf">Say</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">"I'm parent type."</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">child</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">parent</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">p</span> <span class="o">:=</span> <span class="nx">parent</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span> <span class="o">:=</span> <span class="nx">child</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nf">Say</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nf">Say</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//I'm parent type.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//I'm parent type.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>これで <code>child</code> 型は <code>parent</code> 型を(擬似的に)継承しているように見える。
更に <code>child</code> 型に対して</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="nx">child</span><span class="p">)</span> <span class="nf">Say</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">"I'm child type."</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>という関数を追加すれば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">parent</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="nx">parent</span><span class="p">)</span> <span class="nf">Say</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">"I'm parent type."</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">child</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">parent</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="nx">child</span><span class="p">)</span> <span class="nf">Say</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="k">return</span> <span class="s">"I'm child type."</span>
</span></span><span class="line hl"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">p</span> <span class="o">:=</span> <span class="nx">parent</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span> <span class="o">:=</span> <span class="nx">child</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nf">Say</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nf">Say</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//I'm parent type.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//I'm child type.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div>
<p>という感じになり <code>child</code> 型の <code>Say()</code> 関数が <code>parent</code> 型の <code>Say()</code> 関数をオーバーライドしているように見える。
ところが <code>parent</code> 型に</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="nx">parent</span><span class="p">)</span> <span class="nf">Speek</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nf">Say</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>という関数を追加し,これを使って喋らせてみると</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">parent</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="nx">parent</span><span class="p">)</span> <span class="nf">Say</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">"I'm parent type."</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line hl"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="nx">parent</span><span class="p">)</span> <span class="nf">Speek</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nf">Say</span><span class="p">())</span>
</span></span><span class="line hl"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">child</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">parent</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">c</span> <span class="nx">child</span><span class="p">)</span> <span class="nf">Say</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">"I'm child type."</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">c</span> <span class="o">:=</span> <span class="nx">child</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">c</span><span class="p">.</span><span class="nf">Say</span><span class="p">())</span>
</span></span><span class="line hl"><span class="cl"> <span class="nx">c</span><span class="p">.</span><span class="nf">Speek</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//I'm child type.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//I'm parent type.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span></span></span></code></pre></div>
<p>となり <code>Speek()</code> 関数はあくまでも <code>parent</code> 型の <code>Say()</code> 関数を呼び出していることが分かる。</p>
<p>実は <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の埋め込みフィールドは「継承」ではなく「委譲」として機能する。
したがって C++ や Java の仮想関数のようにメソッドがオーバーライドされることもない。
型の中で関数名が被った際の暗黙の優先順位は決められているためオーバーライドしているように見えるだけなのである。</p>
<p>これを先程の factory method pattern のクラス図に当てはめて考えると <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>で無理やり実装しようとしても</p>
<figure style='margin:0 auto;text-align:center;'><a href="./delegation.puml"><img src="./delegation.png" srcset="./delegation.png 1306w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<p>という感じになり,そもそも <code>Creator</code> を抽象クラスに(polymorphic に)できないし,サブクラスの <code>ConcreteCreator</code> から <code>anOperation()</code> メソッドを呼び出しても内部で呼び出される <code>factoryMethod()</code> メソッドは <code>Creator</code> クラスのものということになる。</p>
<h2>「継承」できないなら「注入」すればいいじゃない</h2>
<p>さて,どうしようか。
とりあえずコードを書きながら考えてみよう。</p>
<p>最初に紹介した記事を参考にしつつ,まずは <code>Product</code> に相当する interface 型を以下のように定義するところから始めようか。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//Human is Product class of factory method pattern
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Human</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">SelfIntroduction</span><span class="p">()</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>続いて <code>ConcreteProduct</code> に相当する型も定義する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//NamedHuman is parent class with name property
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">NamedHuman</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">MyName</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">nh</span> <span class="nx">NamedHuman</span><span class="p">)</span> <span class="nf">Name</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="nx">nh</span><span class="p">.</span><span class="nx">MyName</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="s">"〈名無し〉"</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">nh</span><span class="p">.</span><span class="nx">MyName</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//Man is Concrete Product class of factory method pattern
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Man</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">NamedHuman</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">hm</span> <span class="nx">Man</span><span class="p">)</span> <span class="nf">SelfIntroduction</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">hm</span><span class="p">.</span><span class="nf">Name</span><span class="p">()</span> <span class="o">+</span> <span class="s">"だぜ!"</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">//woman is Concrete Product class of factory method pattern
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Woman</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">NamedHuman</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">hm</span> <span class="nx">Woman</span><span class="p">)</span> <span class="nf">SelfIntroduction</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">hm</span><span class="p">.</span><span class="nf">Name</span><span class="p">()</span> <span class="o">+</span> <span class="s">"よ!"</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>ちょっと分かりにくいかも知れないが,クラス図にすると</p>
<figure style='margin:0 auto;text-align:center;'><a href="./human.puml"><img src="./human.png" srcset="./human.png 1134w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<p>となっていて <code>Man</code> 型および <code>Woman</code> 型が(<code>Human</code> 型に対する) <code>ConcreteProduct</code> クラスに相当するのが分かると思う。
ちょろんと動かしてみよう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewMan</span><span class="p">(</span><span class="nx">name</span> <span class="kt">string</span><span class="p">)</span> <span class="nx">Human</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">m</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">Man</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">m</span><span class="p">.</span><span class="nx">MyName</span> <span class="p">=</span> <span class="nx">name</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">m</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewWoman</span><span class="p">(</span><span class="nx">name</span> <span class="kt">string</span><span class="p">)</span> <span class="nx">Human</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">w</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">Woman</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">w</span><span class="p">.</span><span class="nx">MyName</span> <span class="p">=</span> <span class="nx">name</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">w</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">h1</span> <span class="o">:=</span> <span class="nf">NewMan</span><span class="p">(</span><span class="s">"太郎"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">h2</span> <span class="o">:=</span> <span class="nf">NewWoman</span><span class="p">(</span><span class="s">"花子"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">h1</span><span class="p">.</span><span class="nf">SelfIntroduction</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">h2</span><span class="p">.</span><span class="nf">SelfIntroduction</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//太郎だぜ!
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//花子よ!
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p><a href="https://play.golang.org/p/KYQV48YV2pz" title="The Go Playground">問題なく動作する</a>ことが確認できた。</p>
<p>ここまで書いたコードを眺めてみると <code>NewMan()</code> および <code>NewWoman()</code> 関数が <code>factoryMethod()</code> メソッドと似た機能を有していることが分かる。
じゃあ,これらの関数を実行時に <code>Creator</code> クラスに埋め込むように構成すればいんじゃね?</p>
<p>さっそく書いてみよう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">//Speaker is Creator class of factory method pattern
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Speaker</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">createHuman</span> <span class="kd">func</span><span class="p">(</span><span class="kt">string</span><span class="p">)</span> <span class="nx">Human</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">s</span> <span class="nx">Speaker</span><span class="p">)</span> <span class="nf">Speech</span><span class="p">(</span><span class="nx">name</span> <span class="kt">string</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">hm</span> <span class="o">:=</span> <span class="nx">s</span><span class="p">.</span><span class="nf">createHuman</span><span class="p">(</span><span class="nx">name</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">hm</span><span class="p">.</span><span class="nf">SelfIntroduction</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>createHuman</code> は関数型のメンバ変数で <code>func(string) Human</code> のインタフェースを持つ関数を表す。
これはすなわち <code>NewMan()</code> および <code>NewWoman()</code> 関数のことである。</p>
<p>それでは実際に動かしてみよう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">NewSpeeker</span><span class="p">(</span><span class="nx">f</span> <span class="kd">func</span><span class="p">(</span><span class="kt">string</span><span class="p">)</span> <span class="nx">Human</span><span class="p">)</span> <span class="o">*</span><span class="nx">Speaker</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">&</span><span class="nx">Speaker</span><span class="p">{</span><span class="nx">createHuman</span><span class="p">:</span> <span class="nx">f</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s1</span> <span class="o">:=</span> <span class="nf">NewSpeeker</span><span class="p">(</span><span class="nx">NewMan</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s2</span> <span class="o">:=</span> <span class="nf">NewSpeeker</span><span class="p">(</span><span class="nx">NewWoman</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s1</span><span class="p">.</span><span class="nf">Speech</span><span class="p">(</span><span class="s">"太郎"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s2</span><span class="p">.</span><span class="nf">Speech</span><span class="p">(</span><span class="s">"花子"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="c1">//Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//太郎だぜ!
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="c1">//花子よ!
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p><a href="https://play.golang.org/p/_QFnKteL_c6" title="The Go Playground">よーし,うむうむ,よーし</a>。
クラス図にするとこんな感じだろうか(factory method pattern のクラス図と比較してみよう)。</p>
<figure style='margin:0 auto;text-align:center;'><a href="./injection.puml"><img src="./injection.png" srcset="./injection.png 1168w" sizes="(min-width:600px) 500px, 80vw" alt="" loading="lazy"></a></figure>
<p>これってだいぶ不格好だけど,まさしく依存(オブジェクト)の注入(dependency injection pattern)だよね<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。</p>
<p>ていうか <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>プログラマは error 型や標準パッケージの <a href="https://golang.org/pkg/io/" title="io - The Go Programming Language"><code>io</code></a><code>.Reader</code> などを通じて息をするように「依存の注入」を行っているわけで,「継承できないなら注入すればいいじゃない」という感じで,自国語に得意なパターンへ持ち込むのが得策だと思う。</p>
<h2>ブックマーク</h2>
<ul>
<li>
<p><a href="https://ja.wikipedia.org/wiki/Factory_Method_%E3%83%91%E3%82%BF%E3%83%BC%E3%83%B3">Factory Method パターン - Wikipedia</a> : クラス図はこちらを参考にした</p>
</li>
<li>
<p><a href="https://en.wikipedia.org/wiki/Dependency_injection">Dependency injection - Wikipedia</a> : 英語版と日本語版でかなり内容が異なるのだが大丈夫か?</p>
</li>
<li>
<p><a href="https://text.baldanders.info/golang/object-oriented-programming/">Go 言語における「オブジェクト」</a></p>
</li>
</ul>
<!--
- [The Go Playground](https://play.golang.org/p/lQlNziXLf9F)
-->
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B00I8ATHGW?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/41mh5r0NwLL._SL160_.jpg" width="126" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B00I8ATHGW?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">増補改訂版 Java言語で学ぶデザインパターン入門</a></dt>
<dd>結城 浩 (著)</dd>
<dd>SBクリエイティブ 2004-06-18 (Release 2014-03-12)</dd>
<dd>Kindle版</dd>
<dd>B00I8ATHGW (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">結城浩さんによる通称「デザパタ本」。 Java 以外でも使える優れもの。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2016-01-05">2016-01-05</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- 増補改訂版 Java言語で学ぶデザインパターン入門 -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>Interface 型の性質を示すものとして <a href="https://en.wikipedia.org/wiki/Duck_typing" title="Duck typing - Wikipedia">duck typing</a> がよく挙げられる。私も以前はそう思っていたが <a href="https://en.wikipedia.org/wiki/Duck_typing" title="Duck typing - Wikipedia">duck typing</a> は動的型付き言語で型を決定する手法(のひとつ)を指すのだそうだ。もちろん <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>は静的型付き言語なのでこれに当てはまらない。じゃあ <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の interface 型は何かというと<a href="https://methane.hatenablog.jp/entry/ruby-python-go-typing" title="Python と Ruby と typing - methaneのブログ">「構造的部分型」と呼ぶのが正しい</a>ようだ。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>私は dependency injection を「依存性の注入」と訳すことに懐疑的だという<a href="http://blog.a-way-out.net/blog/2015/08/31/your-dependency-injection-is-wrong-as-I-expected/" title="やはりあなた方のDependency Injectionはまちがっている。 — A Day in Serenity (Reloaded) — PHP, FuelPHP, Linux or something">主張</a>に同意します。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Interface の謎
tag:text.Baldanders.info,2016-02-22:/golang/interface/
2016-02-22T10:22:33+00:00
2021-11-28T02:58:44+00:00
Interface には落とし穴がある。
Spiegel
https://baldanders.info/profile/
<p>今回も軽めの小ネタで。</p>
<p>まず,文字列の配列を表示するだけの単純なコードを書いてみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">strlist</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"alpha"</span><span class="p">,</span> <span class="s">"beta"</span><span class="p">,</span> <span class="s">"gamma"</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">strlist</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>結果は</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">[alpha beta gamma]
</span></span></code></pre></div><p>となる。
配列<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> の中身をそのままダンプ出力しているだけなので,まぁ当たり前っちゃあ当たり前。
では,配列のダンプではなくきちんと項目を列挙したいとしよう。
やり方は色々あるが簡単に “<code>...</code>” トークンを使って</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">strlist</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"alpha"</span><span class="p">,</span> <span class="s">"beta"</span><span class="p">,</span> <span class="s">"gamma"</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">strlist</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>と配列を展開すればいんじゃね? って思うよね,普通。
<a href="https://golang.org/pkg/fmt/" title="fmt - The Go Programming Language"><code>fmt</code></a><code>.Println()</code> 関数の定義を見ても</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Println</span><span class="p">(</span><span class="nx">a</span> <span class="o">...</span><span class="kd">interface</span><span class="p">{})</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
</span></span></code></pre></div><p>となっているし,問題ないように見える。</p>
<p>でもこれはうまくいかない。
これを実行しようとすると</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">prog.go:7: cannot use strlist (type []string) as type []interface {} in argument to fmt.Println
</span></span></code></pre></div><p>とエラーになる。</p>
<p>実は <code>[]string</code> 型の <code>strlist</code> は <a href="https://golang.org/pkg/fmt/" title="fmt - The Go Programming Language"><code>fmt</code></a><code>.Println()</code> 関数に渡す際に <code>[]interface{}</code> 型ではなく <code>interface{}</code> 型に<strong>必ず</strong>キャストされる。
だから <code>strlist...</code> と展開しようとしても「そりゃあ無理(←超意訳)」と怒られてしまうわけだ。
<a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の型(<a href="https://go.dev/ref/spec#Properties_of_types_and_values" title="Properties of types and values">type</a>)は</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Msg</span> <span class="p">[]</span><span class="kt">string</span>
</span></span></code></pre></div><p>のように配列やポインタも型として定義できてしまうことを<a href="https://text.baldanders.info/golang/object-oriented-programming/">思い出して</a>欲しい。</p>
<p>じゃあ,明示的なキャストならいけるのかと思ったが</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Msg</span> <span class="p">[]</span><span class="kt">string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">strlist</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"alpha"</span><span class="p">,</span> <span class="s">"beta"</span><span class="p">,</span> <span class="s">"gamma"</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(([]</span><span class="kd">interface</span><span class="p">{})(</span><span class="nx">strlist</span><span class="p">)</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>結果は</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">prog.go:9: cannot convert strlist (type []string) to type []interface {}
</span></span></code></pre></div><p>と,これもエラーになった。</p>
<p>ではどうすればいいのかというと <code>[]interface{}</code> 型の配列を用意してそこに値をコピーする。
先程のコードであれば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Msg</span> <span class="p">[]</span><span class="kt">string</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">strlist</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">string</span><span class="p">{</span><span class="s">"alpha"</span><span class="p">,</span> <span class="s">"beta"</span><span class="p">,</span> <span class="s">"gamma"</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">list</span> <span class="p">=</span> <span class="nb">make</span><span class="p">([]</span><span class="kd">interface</span><span class="p">{},</span> <span class="mi">0</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">for</span> <span class="nx">_</span><span class="p">,</span> <span class="nx">str</span> <span class="o">:=</span> <span class="k">range</span> <span class="nx">strlist</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">list</span> <span class="p">=</span> <span class="nb">append</span><span class="p">(</span><span class="nx">list</span><span class="p">,</span> <span class="nx">str</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">list</span><span class="o">...</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>とすれば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">alpha beta gamma
</span></span></code></pre></div><p>のようにめでたく列挙される。
この問題は <a href="https://golang.org/pkg/fmt/" title="fmt - The Go Programming Language"><code>fmt</code></a><code>.Println()</code> 関数だけじゃなく,ある型の配列を <code>[]interface{}</code> 型にキャストしようとする際は必ず発生するようだ<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。</p>
<p>いや,「“<code>cannot use strlist (type []string) as type []interface {} in argument to fmt.Println</code>” なんてコンパイルエラーを出せるならコンパイラ側でなんとかしてよ」と思うのだが,どうも無理らしい。</p>
<p>やれやれ。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="https://medium.com/@tucnak/why-go-is-a-poorly-designed-language-1cc04e5daf2#.ucutrogyz">Why Go is a poorly designed language — Medium</a> (<a href="http://postd.cc/why-go-is-a-poorly-designed-language/">日本語訳</a>)</li>
<li><a href="https://github.com/golang/go/wiki/InterfaceSlice">InterfaceSlice · golang/go Wiki</a></li>
</ul>
<p><a href="https://text.baldanders.info/golang/bookmark/">Go 言語に関するブックマーク集はこちら</a>。</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>厳密には <a href="http://golang.org/ref/spec#Slice_types">slice</a>。分かってますよ,もちろん。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>例えば <code>list</code> に <code>strlist</code> の内容をコピーする際に for 文で回すのではなく <code>list = append(list, strlist...)</code> でできるかどうか試してみればいい。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
Go 言語における「オブジェクト」
tag:text.Baldanders.info,2015-12-15:/golang/object-oriented-programming/
2015-12-15T12:19:58+00:00
2021-11-28T02:58:44+00:00
Go 言語がいわゆる「オブジェクト指向言語」と言えるかどうかについては色々とあるようだが,オブジェクト指向プログラミングを助けるための仕掛けはいくつか存在する。今回はその中の type キーワードを中心に解説していく。
Spiegel
https://baldanders.info/profile/
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>がいわゆる「オブジェクト指向言語」と言えるかどうかについては色々とあるようだが,オブジェクト指向プログラミングを助けるための仕掛けはいくつか存在する。
今回はその中の <a href="https://go.dev/ref/spec#Properties_of_types_and_values" title="Properties of types and values">type</a> キーワードを中心に解説していく。</p>
<p>なお,今回のソースコードは “<a href="https://tour.golang.org/">A Tour of Go</a>” のものをかなり流用しているため取り扱いに注意。
<a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の公式ドキュメントは CC License の by 3.0,ソースコードは <a href="https://golang.org/LICENSE">BSD license</a> で提供されている。</p>
<h2>Go 言語の基本型</h2>
<p>今さらだけど, <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の基本型(basic type)は以下の通り。</p>
<ul>
<li>bool</li>
<li>string</li>
<li>int, int8, int16, int32, int64</li>
<li>uint, uint8, uint16, uint32, uint64</li>
<li>uintptr</li>
<li>byte</li>
<li>rune</li>
<li>float32, float64</li>
<li>complex64, complex128</li>
</ul>
<p>このうち byte は uint8 の別名で rune<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> は int32 の別名である。
また int, uint, uintptr のサイズはプラットフォーム依存になっている。
string は不変(immutable)な値で,その実体は byte 配列である。
基本型は組み込み型であり,振る舞いを追加・変更することはできない。</p>
<p>さらにこれらの基本型を集約した構造体 <a href="https://go.dev/ref/spec#Struct_types" title="Struct types">struct</a> を定義できる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">vertex</span> <span class="o">:=</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">X</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Y</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="p">}{</span><span class="nx">X</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">Y</span><span class="p">:</span> <span class="mi">2</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">vertex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>ちなみに構造体のフィールド(field)には構造体を含めることができ,入れ子構造にすることもできる。</p>
<p>この他に配列(slice<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> を含む)や連想配列(map)あるいは関数値(function value)といったものもあるが,今回は踏み込まない。</p>
<h2>型に名前を付ける</h2>
<p>全ての型には <a href="https://go.dev/ref/spec#Properties_of_types_and_values" title="Properties of types and values">type</a> キーワードを使った宣言(type declaration)により名前を付けることができる。
例えば先ほどのコードは</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Vertex</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">X</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Y</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">vertex</span> <span class="o">:=</span> <span class="nx">Vertex</span><span class="p">{</span><span class="nx">X</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">Y</span><span class="p">:</span> <span class="mi">2</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">vertex</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>と書き直すことができる。</p>
<p><a href="https://go.dev/ref/spec#Properties_of_types_and_values" title="Properties of types and values">type</a> 宣言が使えるのは構造体だけではない。
上述の基本型も <a href="https://go.dev/ref/spec#Properties_of_types_and_values" title="Properties of types and values">type</a> 宣言を使って型を再定義できる。</p>
<p>たとえば,2つの時点間の時間を表す <a href="https://golang.org/pkg/time/" title="time - The Go Programming Language"><code>time</code></a><code>.Duration</code> は以下のように定義されている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Duration</span> <span class="kt">int64</span>
</span></span></code></pre></div><p>また,配列なども型として再定義できる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Msgs</span> <span class="p">[]</span><span class="kt">string</span>
</span></span></code></pre></div><p><a href="https://go.dev/ref/spec#Properties_of_types_and_values" title="Properties of types and values">type</a> 宣言を使って型に名前を付ける利点は3つある。</p>
<ol>
<li>名前を付けることでコードの可読性を上げる(オブジェクト指向設計では名前がとても重要)</li>
<li>再利用性の向上(特に構造体の場合)</li>
<li>型に関数を関連付けることができる。</li>
</ol>
<p><a href="https://go.dev/ref/spec#Properties_of_types_and_values" title="Properties of types and values">type</a> 宣言による名付けは単なる別名定義ではないということだ<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>。</p>
<h2>型に関数を関連付ける</h2>
<p>型に関数を関連付けるには以下のように記述する。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">v</span> <span class="nx">Vertex</span><span class="p">)</span> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="s">"X ="</span><span class="p">,</span> <span class="nx">v</span><span class="p">.</span><span class="nx">X</span><span class="p">,</span> <span class="s">", Y ="</span><span class="p">,</span> <span class="nx">v</span><span class="p">.</span><span class="nx">Y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>(v Vertex)</code> の部分はメソッド・レシーバ(method receiver)と呼ばれ,これが型と関数を関連付ける役割を果たす。
内部処理としては</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="nx">Vertex</span><span class="p">.</span><span class="nf">String</span><span class="p">(</span><span class="nx">v</span> <span class="nx">Vertex</span><span class="p">)</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="s">"X ="</span><span class="p">,</span> <span class="nx">v</span><span class="p">.</span><span class="nx">X</span><span class="p">,</span> <span class="s">", Y ="</span><span class="p">,</span> <span class="nx">v</span><span class="p">.</span><span class="nx">Y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>と等価である<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>。
関数の呼び出し側は</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Vertex</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">X</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Y</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">v</span> <span class="nx">Vertex</span><span class="p">)</span> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="s">"X = "</span><span class="p">,</span> <span class="nx">v</span><span class="p">.</span><span class="nx">X</span><span class="p">,</span> <span class="s">", Y = "</span><span class="p">,</span> <span class="nx">v</span><span class="p">.</span><span class="nx">Y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">vertex</span> <span class="o">:=</span> <span class="nx">Vertex</span><span class="p">{</span><span class="nx">X</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">Y</span><span class="p">:</span> <span class="mi">2</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">vertex</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>のようにインスタンスと関数をピリオドで連結して記述する<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>。</p>
<p>構造体そのものには関数を付与できない<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>。
たとえば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">v</span> <span class="kd">struct</span><span class="p">{</span> <span class="nx">X</span><span class="p">,</span> <span class="nx">Y</span> <span class="kt">int</span> <span class="p">})</span> <span class="nf">String</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="s">"X ="</span><span class="p">,</span> <span class="nx">v</span><span class="p">.</span><span class="nx">X</span><span class="p">,</span> <span class="s">", Y ="</span><span class="p">,</span> <span class="nx">v</span><span class="p">.</span><span class="nx">Y</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kd">var</span> <span class="nx">vertex</span> <span class="p">=</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">X</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Y</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"> <span class="p">}{</span><span class="nx">X</span><span class="p">:</span> <span class="mi">1</span><span class="p">,</span> <span class="nx">Y</span><span class="p">:</span> <span class="mi">2</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">vertex</span><span class="p">.</span><span class="nf">String</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>などと書いても,コンパイル時に</p>
<pre tabindex="0"><code>invalid receiver type struct { X int; Y int } (struct { X int; Y int } is an unnamed type)
</code></pre><p>と怒られる。
<a href="https://go.dev/ref/spec#Properties_of_types_and_values" title="Properties of types and values">type</a> 宣言によって型に名前が付けられていることが重要なのだ。</p>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>には class キーワードはないが, <a href="https://go.dev/ref/spec#Properties_of_types_and_values" title="Properties of types and values">type</a> 宣言を使うことで,名前と属性と操作を持つクラスを記述することができる<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>。</p>
<h2>汎化・特化と処理の委譲</h2>
<p>オブジェクト指向設計においてクラス間の関係は大きく2つある。</p>
<ol>
<li>汎化・特化<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>(継承または is-a 関係)</li>
<li>関連<sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup>(包含または has-a 関係)</li>
</ol>
<p>このうち関連についてはこれまで説明した方法で実現できるが,汎化・特化は表現できない。
そこで以下の機能を使って汎化・特化を実現する。</p>
<h3>振る舞いのみを定義した型</h3>
<p><a href="https://go.dev/ref/spec#Interface_types" title="Interface types">interface</a> を使うと振る舞いのみを定義した汎化クラスを表現することができる。</p>
<p><a href="https://go.dev/ref/spec#Interface_types" title="Interface types">interface</a> で定義された型で最もよく目にするのは <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> だろう。
<a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> は以下のように定義できる<sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="kt">error</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>定義が示すように <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> は「string 型を返す <code>Error()</code> 関数」のみが定義されている。
逆に言うと「string 型を返す <code>Error()</code> 関数」を持つ全ての型は <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> の一種(つまり is-a 関係)であると見なすことができる<sup id="fnref:11"><a href="#fn:11" class="footnote-ref" role="doc-noteref">11</a></sup>。</p>
<p>たとえば</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">os</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// PathError records an error and the operation and file path that caused it.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">PathError</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Op</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Path</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Err</span> <span class="kt">error</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">e</span> <span class="o">*</span><span class="nx">PathError</span><span class="p">)</span> <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">e</span><span class="p">.</span><span class="nx">Op</span> <span class="o">+</span> <span class="s">" "</span> <span class="o">+</span> <span class="nx">e</span><span class="p">.</span><span class="nx">Path</span> <span class="o">+</span> <span class="s">": "</span> <span class="o">+</span> <span class="nx">e</span><span class="p">.</span><span class="nx">Err</span><span class="p">.</span><span class="nf">Error</span><span class="p">()</span> <span class="p">}</span>
</span></span></code></pre></div><p>と定義される <a href="https://golang.org/pkg/os/" title="os - The Go Programming Language"><code>os</code></a><code>.PathError</code> は <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> の一種である。</p>
<p><a href="https://go.dev/ref/spec#Interface_types" title="Interface types">interface</a> も <a href="https://go.dev/ref/spec#Properties_of_types_and_values" title="Properties of types and values">type</a> 宣言で名前を付けることができ,他の型と同じように扱うことができる。
さらに <a href="https://go.dev/ref/spec#Interface_types" title="Interface types">interface</a> で定義した型は振る舞いのみで具体的な実装を含まないため,多態性を持たせた記述が可能になる<sup id="fnref:12"><a href="#fn:12" class="footnote-ref" role="doc-noteref">12</a></sup>。</p>
<h3>将来バージョンで総称型(generics)がサポートされるかも。</h3>
<p>現行版の <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>ではいわゆる<a href="https://golang.org/doc/faq#generics">「総称型」はサポートされていない</a>が,将来バージョンで導入される可能性がある。</p>
<ul>
<li><a href="https://text.baldanders.info/golang/generics-in-go-2/">次期 Go 言語で導入される(かもしれない)総称型について予習する</a></li>
</ul>
<h3>型の埋め込み</h3>
<p>もうひとつの汎化・特化の機能が型の埋め込み(embedding)である。
構造体や <a href="https://go.dev/ref/spec#Interface_types" title="Interface types">interface</a> には別の型を埋め込むことができる。</p>
<p>たとえば <a href="https://golang.org/pkg/io/" title="io - The Go Programming Language"><code>io</code></a><code>.ReadWriter</code> は以下のように <code>Reader</code> および <code>Writer</code> を埋め込んでいる。
(このときの <code>Reader</code> および <code>Writer</code> を「埋め込みインタフェース(embedding interface)」と呼ぶ)</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">io</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Implementations must not retain p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Reader</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Read</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Implementations must not retain p.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">Writer</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Write</span><span class="p">(</span><span class="nx">p</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">,</span> <span class="nx">err</span> <span class="kt">error</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ReadWriter is the interface that groups the basic Read and Write methods.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">ReadWriter</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Reader</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Writer</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これによって <code>ReadWriter</code> は <code>Read()</code> および <code>Write()</code> を自身の振る舞いのように扱うことができる。
この場合も <code>ReadWriter</code> は <code>Reader</code> および <code>Writer</code> の一種であると見なすことができる。</p>
<p>同様に <a href="https://golang.org/pkg/bufio/" title="io - The Go Programming Language"><code>bufio</code></a><code>.ReadWriter</code> についても</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">bufio</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// ReadWriter stores pointers to a Reader and a Writer.
</span></span></span><span class="line"><span class="cl"><span class="c1">// It implements io.ReadWriter.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">ReadWriter</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span><span class="nx">Reader</span>
</span></span><span class="line"><span class="cl"> <span class="o">*</span><span class="nx">Writer</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// NewReadWriter allocates a new ReadWriter that dispatches to r and w.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">NewReadWriter</span><span class="p">(</span><span class="nx">r</span> <span class="o">*</span><span class="nx">Reader</span><span class="p">,</span> <span class="nx">w</span> <span class="o">*</span><span class="nx">Writer</span><span class="p">)</span> <span class="o">*</span><span class="nx">ReadWriter</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">&</span><span class="nx">ReadWriter</span><span class="p">{</span><span class="nx">r</span><span class="p">,</span> <span class="nx">w</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>と実装されていて, <a href="https://golang.org/pkg/bufio/" title="io - The Go Programming Language"><code>bufio</code></a> の <code>Reader</code> および <code>Writer</code> を埋め込み,これらの型の一種として実装されている。
このときの <code>Reader</code> および <code>Writer</code> を「埋め込みフィールド(embedded field)」または「匿名フィールド(anonymous field)」と呼ぶ。
<a href="https://golang.org/pkg/bufio/" title="io - The Go Programming Language"><code>bufio</code></a><code>.ReadWriter</code> は <a href="https://golang.org/pkg/io/" title="io - The Go Programming Language"><code>io</code></a><code>.ReadWriter</code> の一種として機能している点にも注目してほしい。</p>
<h3>関数のオーバーライドと処理の委譲</h3>
<p>では,今まで述べたことを使って以下のコードを書いてみる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">ErrorInfo</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kt">error</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Errno</span><span class="p">()</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">ErrorInfo1</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">err</span> <span class="o">*</span><span class="nx">ErrorInfo1</span><span class="p">)</span> <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="s">"Error Information: "</span><span class="p">,</span> <span class="nx">err</span><span class="p">.</span><span class="nf">Errno</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">err</span> <span class="o">*</span><span class="nx">ErrorInfo1</span><span class="p">)</span> <span class="nf">Errno</span><span class="p">()</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Action</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">ErrorInfo1</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">Action</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Normal End"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> の拡張として <code>ErrorInfo</code> <a href="https://go.dev/ref/spec#Interface_types" title="Interface types">interface</a> を定義する。
<code>ErrorInfo</code> <a href="https://go.dev/ref/spec#Interface_types" title="Interface types">interface</a> では <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> を埋め込み,さらに <code>Errno()</code> 関数を追加定義している。
更に <code>ErrorInfo</code> <a href="https://go.dev/ref/spec#Interface_types" title="Interface types">interface</a> に適合するクラスとして <code>ErrorInfo1</code> 型を作った。
このコードの実行すると “<code>Error Information: 1</code>” が出力される。</p>
<figure style='margin:0 auto;text-align:center;'><a href="./interface.svg"><img src="./interface.svg" srcset="./interface.svg 500w" sizes="(min-width:600px) 500px, 80vw" alt="ErrorInfo interface" loading="lazy"></a><figcaption><div><a href="./interface.svg">ErrorInfo interface</a></div></figcaption>
</figure>
<p>次に <code>ErrorInfo1</code> のバリエーションとして <code>ErrorInfo2</code> を追加してみよう。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">ErrorInfo</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="kt">error</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Errno</span><span class="p">()</span> <span class="kt">int</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">ErrorInfo1</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">err</span> <span class="o">*</span><span class="nx">ErrorInfo1</span><span class="p">)</span> <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprint</span><span class="p">(</span><span class="s">"Error Information: "</span><span class="p">,</span> <span class="nx">err</span><span class="p">.</span><span class="nf">Errno</span><span class="p">())</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">err</span> <span class="o">*</span><span class="nx">ErrorInfo1</span><span class="p">)</span> <span class="nf">Errno</span><span class="p">()</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">1</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">ErrorInfo2</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ErrorInfo1</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">err</span> <span class="o">*</span><span class="nx">ErrorInfo2</span><span class="p">)</span> <span class="nf">Errno</span><span class="p">()</span> <span class="kt">int</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="mi">2</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Action</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="o">:=</span> <span class="o">&</span><span class="nx">ErrorInfo2</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">Action</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Normal End"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><code>ErrorInfo2</code> では <code>Error()</code> 関数は <code>ErrorInfo1</code> のものをそのまま使い回したいが <code>Errno()</code> 関数では異なる値を出力したい,と考えた。
そこで <code>Errno()</code> 関数をオーバライドしようと考えた。
図で書くと以下のような構成を期待したわけだ。</p>
<figure style='margin:0 auto;text-align:center;'><a href="./override.svg"><img src="./override.svg" srcset="./override.svg 518w" sizes="(min-width:600px) 500px, 80vw" alt="Override ?" loading="lazy"></a><figcaption><div><a href="./override.svg">Override ?</a></div></figcaption>
</figure>
<p>しかし実際には,期待した “<code>Error Information: 2</code>” ではなく,前回と同じ “<code>Error Information: 1</code>” が出力される。</p>
<p>上のコードでは <code>ErrorInfo2</code> と直接関連付けられた <code>Error()</code> がないため <code>ErrorInfo1</code> の <code>Error()</code> が呼ばれるが,その関数の中で呼ばれる <code>Errno()</code> は <code>ErrorInfo2</code> と関連付けられた関数ではなく <code>ErrorInfo1</code> と関連付けられた関数になる。</p>
<figure style='margin:0 auto;text-align:center;'><a href="./delegation.svg"><img src="./delegation.svg" srcset="./delegation.svg 518w" sizes="(min-width:600px) 500px, 80vw" alt="delegation" loading="lazy"></a><figcaption><div><a href="./delegation.svg">delegation</a></div></figcaption>
</figure>
<p>これは <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>では埋め込みフィールドの関数呼び出しが「委譲」として機能しているためである。
たとえば C++ 言語では virtual 修飾子を付与して仮想関数化することで意図的にオーバーライドできるが<sup id="fnref:13"><a href="#fn:13" class="footnote-ref" role="doc-noteref">13</a></sup>, <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>ではこのような仕掛けがないため<sup id="fnref:14"><a href="#fn:14" class="footnote-ref" role="doc-noteref">14</a></sup>,呼ばれた関数は常に委譲として機能する<sup id="fnref:15"><a href="#fn:15" class="footnote-ref" role="doc-noteref">15</a></sup>。</p>
<p>上の例はクラス構成からして明らかにダメダメなのだが,今回のポイントはサブクラスである <code>ErrorInfo2</code> から <code>Errno()</code> 関数を上書きすることでスーパークラス <code>ErrorInfo1</code> の <code>Error()</code> 関数の処理を書き換えようとした点にある。
継承<sup id="fnref:16"><a href="#fn:16" class="footnote-ref" role="doc-noteref">16</a></sup> の実装で一番よくあるミスがこの「カプセル化の破れ」で, <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の場合は敢えて移譲を強制することでこの手の不具合が発生するのを回避しようとしているように見える。</p>
<p>他の言語では明示的に委譲を実装しようとすると冗長な記述になることが多いが, <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の場合は埋め込みを使うことでシンプルな記述で委譲を実装できる点がメリットと言える。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="http://mattn.kaoriya.net/software/lang/go/20130919023425.htm">Big Sky :: Go言語でインタフェースの変更がそれ程問題にならない理由</a></li>
<li><a href="http://qiita.com/shibukawa/items/16acb36e94cfe3b02aa1">オブジェクト指向言語としてGolangをやろうとするとハマること - Qiita</a>
<ul>
<li><a href="http://qiita.com/sona-tar/items/2b4b70694fd680f6297c">オブジェクト指向言語としてGolangをやろうとするとハマる点を整理してみる - Qiita</a></li>
</ul>
</li>
<li><a href="http://otiai10.hatenablog.com/entry/2014/01/15/220136">Go言語に継承は無いんですか【golang】 - DRYな備忘録</a></li>
<li><a href="http://otiai10.hatenablog.com/entry/2014/06/16/224109">Go言語でジェネリクスっぽいことがしたいでござる【generics】【golang】 - DRYな備忘録</a></li>
<li><a href="https://skatsuta.github.io/2015/12/29/value-receiver-pointer-receiver/">Go 言語の値レシーバとポインタレシーバ | Step by Step</a></li>
<li><a href="http://qiita.com/tenntenn/items/bba69d84a1e0b67c4db8">埋込みとインタフェース #golang - Qiita</a></li>
<li><a href="http://qiita.com/ruiu/items/22910a4bae6cb716a391">Goにatexitやグローバルなデストラクタがない理由 - Qiita</a></li>
<li><a href="http://methane.hatenablog.jp/entry/ruby-python-go-typing">Python と Ruby と typing - methaneのブログ</a> : <a href="https://go.dev/ref/spec#Interface_types" title="Interface types">interface</a> を使った汎化・特化の関係は duck typing ではなく「構造的部分型(structural subtyping)」と言うのが正しいらしい</li>
</ul>
<p><a href="https://text.baldanders.info/golang/bookmark/">Go 言語に関するブックマーク集はこちら</a>。</p>
<h2>参考図書</h2>
<div class="hreview">
<div class="photo"><a href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1"><img src="https://m.media-amazon.com/images/I/416Stewy0NS._SL160_.jpg" width="123" alt="photo"></a></div>
<dl>
<dt class="item"><a class="fn url" href="https://www.amazon.co.jp/dp/B099928SJD?tag=baldandersinf-22&linkCode=ogi&th=1&psc=1">プログラミング言語Go</a></dt>
<dd>アラン・ドノバン (著), ブライアン・カーニハン (著), 柴田芳樹 (著)</dd>
<dd>丸善出版 2016-06-20 (Release 2021-07-13)</dd>
<dd>Kindle版</dd>
<dd>B099928SJD (ASIN)</dd>
<dd>評価<abbr class="rating fa-sm" title="5"> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i> <i class="fas fa-star"></i></abbr></dd>
</dl>
<p class="description">Kindle 版出た! 一部内容が古びてしまったが,この本は Go 言語の教科書と言ってもいいだろう。感想は<a href="https://text.baldanders.info/remark/2016/07/go-programming-language/" >こちら</a>。</p>
<p class="powered-by">reviewed by <a href='#maker' class='reviewer'>Spiegel</a> on <abbr class="dtreviewed" title="2021-05-22">2021-05-22</abbr> (powered by <a href="https://affiliate.amazon.co.jp/assoc_credentials/home">PA-APIv5</a>)</p>
</div> <!-- プログラミング言語Go -->
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>rune は Unicode 文字の符号点(code point)を示す型で文字そのものを表現する。 string と rune の関係については「<a href="https://text.baldanders.info/golang/string-and-rune/">String と Rune</a>」を参照のこと。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>配列と slice の関係については「<a href="https://text.baldanders.info/golang/array-and-slice/">配列と Slice</a>」で紹介している。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p>型の別名定義はバージョン 1.9 から導入された。詳しくは「<a href="https://text.baldanders.info/golang/go-1_9-and-type-alias/">Go 1.9 と Type Alias</a>」を参照のこと <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:4">
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の関数呼び出しにおいて引数の渡し方は基本的に「値渡し」である。詳しくは「<a href="https://text.baldanders.info/golang/function-and-pointer/">関数とポインタ</a>」を参照のこと。 <a href="#fnref:4" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:5">
<p>ちなみに <a href="https://golang.org/pkg/fmt/" title="time - The Go Programming Language"><code>fmt</code></a><code>.Print</code> などでは引数の型が <code>String()</code> を持っていることを期待し,この関数の出力結果をデフォルト書式にしている。したがって <code>fmt.Println(vertex)</code> と書いても同じ結果になる。 <a href="#fnref:5" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:6">
<p>他にも基本型や他パッケージで定義されている型に関数を追加することはできない。当たり前だけど。 <a href="#fnref:6" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:7">
<p>クラスは名前と属性と操作の3つの要素で構成されている。名前は他クラスと識別できるものを1個。属性と操作は0個以上存在する。 <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>では空のフィールドの <a href="https://go.dev/ref/spec#Struct_types" title="Struct types">struct</a> を定義することにより0個の属性を持つクラスを構成できる。 <a href="#fnref:7" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:8">
<p>言わずもがなだが,サブクラスから見たスーパークラスが「汎化」でその逆が「特化」である。 <a href="#fnref:8" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:9">
<p>関連は更に集約と複合に分類できるが今回は踏み込まない。 <a href="#fnref:9" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:10">
<p><a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> は組み込み型なので,実際にこのような定義が標準パッケージにあるわけではない。 <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> について詳しくは「<a href="https://text.baldanders.info/golang/error-handling/">エラー・ハンドリングについて</a>」を参照のこと。 <a href="#fnref:10" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:11">
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>では Java の implement のような interface クラスからの継承を明示するキーワードはない。記述された振る舞いからクラス関係を決定する方法を「構造的部分型(structural subtyping)」と呼ぶ。構造的部分型のメリットのひとつは多重継承で発生する様々な問題(名前の衝突や菱形継承など)を気にする必要がない点である。 <a href="#fnref:11" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:12">
<p>たとえば <code>interface{}</code> と記述すればあらゆる型を含むことになる。これを利用して <a href="https://golang.org/pkg/fmt/" title="time - The Go Programming Language"><code>fmt</code></a><code>.Print</code> は <code>func Print(a ...interface{}) (n int, err error) { ... }</code> と定義されている。 <a href="#fnref:12" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:13">
<p>逆に Java では関数は常に仮想関数として機能しオーバーライドされる可能性がある。これを抑止するためには final 修飾子を付加する。 <a href="#fnref:13" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:14">
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>的には埋め込みフィールドはフィールドのバリエーションのひとつにすぎないため,動作も通常のフィールドが持つ関数を呼び出した場合と変わらない。そういう意味では構造体への埋め込みは,見かけ上は「is-a 関係」でも,実質的には「has-a 関係」に近いと言えるかもしれない。 <a href="#fnref:14" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:15">
<p>複数の型を埋め込んでいる場合,埋め込みフィールド間で名前が衝突しているフィールドや関数を使おうとするとコンパイルエラーになる。この場合は <code>err.ErrorInfo1.Error()</code> のように型を明示して回避できる。 <a href="#fnref:15" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:16">
<p>ここで言う継承は設計時の「汎化・特化」のことではなく,言語機能などを使った実装上の継承のこと。 <a href="#fnref:16" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>
エラー・ハンドリングについて(追記あり)
tag:text.Baldanders.info,2015-09-30:/golang/error-handling/
2015-09-29T15:27:48+00:00
2021-11-28T02:58:44+00:00
C++ や Java のような言語圏から来た(私のような)人間にとって Go 言語の「オブジェクト指向」はかなり異質なのだが,慣れてみると逆にとても合理的に見えてくる。この最たるものが error 型である。(追記あり)
Spiegel
https://baldanders.info/profile/
<p>C++ や Java のような言語圏から来た(私のような)人間にとって <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の「オブジェクト指向」はかなり異質なのだが,慣れてみると逆にとても合理的に見えてくる。
なんで C++ や Java はこのようなアプローチをとらなかったのか不思議なほどである。</p>
<p>この最たるものがエラー・ハンドリングだ。</p>
<h2>Go 言語には「例外」がない</h2>
<p>「例外(exception)」は本来の処理の流れをぶった切って「大域脱出」するための仕組みである。
でも考えてみれば例外というのはかなり微妙な言語仕様だ。</p>
<p>例外が抱える問題というのは本質的に <code>goto</code> 文の問題と同じ<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>。
オブジェクトの状態ごと脱出するため,(脱出前ではなく)脱出後にオブジェクトの後始末を記述せざるを得ないし,記述するためには脱出前の状態(の可能性)を「知識」として知っていなければならない<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>。
もし後始末をきちんとしないと,それがバグやリークやその他の脆弱性のもとになる。</p>
<p><a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>はそんな面倒くさいことは考えない。
どうするかというと,普通に返り値に <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> を返す。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">file</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="nx">filename</span><span class="p">)</span>
</span></span></code></pre></div><p><a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> は無視することもできる<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">file</span><span class="p">,</span> <span class="nx">_</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="nx">filename</span><span class="p">)</span>
</span></span></code></pre></div><p>検出した <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> はその場で処理して抜けてしまえばよい。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">file</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="nx">filename</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>開始処理と終了(後始末)処理が対になっている場合(<code>Open</code>/<code>Close</code> とは限らない)は <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Defer</a> 構文で終了処理を保証する<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="nx">file</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">os</span><span class="p">.</span><span class="nf">Open</span><span class="p">(</span><span class="nx">filename</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">false</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="k">defer</span> <span class="nx">file</span><span class="p">.</span><span class="nf">Close</span><span class="p">()</span>
</span></span></code></pre></div><p>これが <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の基本的な書き方。
特徴的なのは,ある処理に纏わる処理をセットで記述できる点である。
<code>try</code> と <code>catch</code> と <code>finally</code> の間で目線を行ったり来たりさせなくても,「そこ」だけを見れば把握できる。
ある意味でとても文芸的なコード<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> であると言える<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>。</p>
<h2>error</h2>
<p>ここで <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> について改めて説明しておく。
<a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> は以下の形式で表現できる <a href="https://golang.org/doc/effective_go.html#interfaces_and_types" title="Effective Go - The Go Programming Language">interface</a> 型のひとつである<sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// The error built-in interface type is the conventional interface for
</span></span></span><span class="line"><span class="cl"><span class="c1">// representing an error condition, with the nil value representing no error.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="kt">error</span> <span class="kd">interface</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>つまり <code>Error()</code> 関数を持つオブジェクトなら <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> として使える。
これのいちばん簡単な実装が <a href="https://golang.org/pkg/errors/" title="errors - The Go Programming Language"><code>errors</code></a> パッケージである。
<a href="https://golang.org/pkg/errors/" title="errors - The Go Programming Language"><code>errors</code></a> パッケージの中身は以下のようになっている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">errors</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// New returns an error that formats as the given text.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">New</span><span class="p">(</span><span class="nx">text</span> <span class="kt">string</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="o">&</span><span class="nx">errorString</span><span class="p">{</span><span class="nx">text</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// errorString is a trivial implementation of error.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">errorString</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">s</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">e</span> <span class="o">*</span><span class="nx">errorString</span><span class="p">)</span> <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">e</span><span class="p">.</span><span class="nx">s</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これは実体として <a href="http://golang.org/ref/spec#String_types">string</a> 型の property がひとつだけあって <code>Error()</code> 関数で property をそのまま返すというものだ。
<a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の標準パッケージの多くはこの <a href="https://golang.org/pkg/errors/" title="errors - The Go Programming Language"><code>errors</code></a> パッケージを使って <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> を定義している。
たとえば <a href="https://golang.org/pkg/os/" title="os - The Go Programming Language"><code>os</code></a> パッケージの最下位の <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> は以下のように定義されている。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">os</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="s">"errors"</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Portable analogs of some common system call errors.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">var</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ErrInvalid</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"invalid argument"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ErrPermission</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"permission denied"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ErrExist</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"file already exists"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="nx">ErrNotExist</span> <span class="p">=</span> <span class="nx">errors</span><span class="p">.</span><span class="nf">New</span><span class="p">(</span><span class="s">"file does not exist"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span>
</span></span></code></pre></div><p>もう少し複雑な <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> では,以下のように詳細情報を持つものもある。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// PathError records an error and the operation and file path that caused it.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">type</span> <span class="nx">PathError</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Op</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Path</span> <span class="kt">string</span>
</span></span><span class="line"><span class="cl"> <span class="nx">Err</span> <span class="kt">error</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">e</span> <span class="o">*</span><span class="nx">PathError</span><span class="p">)</span> <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span> <span class="k">return</span> <span class="nx">e</span><span class="p">.</span><span class="nx">Op</span> <span class="o">+</span> <span class="s">" "</span> <span class="o">+</span> <span class="nx">e</span><span class="p">.</span><span class="nx">Path</span> <span class="o">+</span> <span class="s">": "</span> <span class="o">+</span> <span class="nx">e</span><span class="p">.</span><span class="nx">Err</span><span class="p">.</span><span class="nf">Error</span><span class="p">()</span> <span class="p">}</span>
</span></span></code></pre></div><h2>3つのエラー・ハンドリング</h2>
<p><a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> を判別する方法としては以下の3つがある。</p>
<ol>
<li><a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> インスタンスを比較する</li>
<li><a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> の型を判別する</li>
<li><code>Error()</code> 関数で出力される文字列を解釈する</li>
</ol>
<h3>インスタンスを比較する</h3>
<p>あらかじめ定義済みの <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> インスタンスがあれば,インスタンスの比較で判別できる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">err</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">os</span><span class="p">.</span><span class="nx">ErrInvalid</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"引数が不正"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">os</span><span class="p">.</span><span class="nx">ErrPermission</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"アクセスは許可できない"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">os</span><span class="p">.</span><span class="nx">ErrExist</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"そのファイルは既にある"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">os</span><span class="p">.</span><span class="nx">ErrNotExist</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"ファイルが存在しない"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>単純にエラーの種類が分かればいいのであれば,この方法が最もシンプル。</p>
<h3>型を判別する</h3>
<p><a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> は <a href="https://golang.org/doc/effective_go.html#interfaces_and_types" title="Effective Go - The Go Programming Language">interface</a> 型なので <a href="https://go.dev/ref/spec#Conversions" title="The Go Programming Language Specification - The Go Programming Language">Conversion</a> 構文で判別できる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">e</span> <span class="o">:=</span> <span class="nx">err</span><span class="p">.(</span><span class="kd">type</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="o">*</span><span class="nx">os</span><span class="p">.</span><span class="nx">PathError</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">errno</span><span class="p">,</span> <span class="nx">ok</span> <span class="o">:=</span> <span class="nx">e</span><span class="p">.</span><span class="nx">Err</span><span class="p">.(</span><span class="nx">syscall</span><span class="p">.</span><span class="nx">Errno</span><span class="p">);</span> <span class="nx">ok</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">switch</span> <span class="nx">errno</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">ENOENT</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"ファイルが存在しない"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">case</span> <span class="nx">syscall</span><span class="p">.</span><span class="nx">ENOTDIR</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"ディレクトリが存在しない"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"Errno ="</span><span class="p">,</span> <span class="nx">errno</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"その他の PathError"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">default</span><span class="p">:</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Fprintln</span><span class="p">(</span><span class="nx">os</span><span class="p">.</span><span class="nx">Stderr</span><span class="p">,</span> <span class="s">"その他のエラー"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p><a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> に状態(status)を持たせる必要がある場合は,この方法を使うべき。</p>
<h3>文字列を比較する</h3>
<p>上述の方法で判別できない場合は <code>Error()</code> 関数で出力される文字列を解釈して処理するしかない。
<a href="http://0xcc.net/misc/bad-knowhow.html">バッドノウハウ</a>。</p>
<h2>エラー・ハンドリングの設計</h2>
<p>エラー・ハンドリングの方針としては,以下の2つのうちのどちらかだろう。</p>
<ol>
<li><a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> を下位ロジックから上位ロジックまで持ち回し,最上位ロジックで最終的な判定と処理を行う</li>
<li>下位ロジックの <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> をカプセル化した新たなインスタンスを生成し上位ロジックに渡す。上位ロジックは直近のロジックの <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> のみが見える</li>
</ol>
<p>最初のやり方は一見よさげだが,この方針では上位ロジックが下位ロジックの全ての <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> を把握している必要があり現実的でない。
またオブジェクト指向設計では “Don’t talk to strangers” の原則があり,いわゆる「友達の友達」のことは知らないふりをするのがよい設計と言われている。</p>
<p>こう考えると文字列での比較は最も下策であると言える。
また,型を判別する場合でも,下位レイヤの状態を生のまま見せるのではなく,必要な情報のみを返す関数を実装するほうが上策と言えるだろう。</p>
<p>もうひとつ考慮すべき点としてエラー・メッセージの設計が挙げられるだろう。
<a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> に対するメッセージをどのように設計するかは(大規模アプリケーションでは特に)重要である。</p>
<p>(「<a href="https://text.baldanders.info/golang/error-handling2/">Error の構造化</a>」および「<a href="https://text.baldanders.info/golang/error-handling-again/">エラー・ハンドリング再考</a>」へ続く)</p>
<h2>【追記】 Panic と Recover</h2>
<p>たとえばゼロ除算を行った場合や配列などで領域外を参照・設定しようとした場合,あるいは allocation に失敗した場合など,致命的なエラーが発生する場合がある。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">f</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">numbers</span> <span class="o">:=</span> <span class="p">[]</span><span class="kt">int</span><span class="p">{</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">numbers</span><span class="p">[</span><span class="mi">3</span><span class="p">])</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これを実行すると</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">C:<span class="se">\w</span>orkspace<span class="se">\g</span>o-practice<span class="se">\s</span>rc<span class="se">\p</span>anic01>go run panic01.go
</span></span><span class="line"><span class="cl">panic: runtime error: index out of range
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">goroutine <span class="m">1</span> <span class="o">[</span>running<span class="o">]</span>:
</span></span><span class="line"><span class="cl">main.f<span class="o">()</span>
</span></span><span class="line"><span class="cl"> C:/workspace/go-practice/src/panic01/panic01.go:12 +0x14a
</span></span><span class="line"><span class="cl">main.main<span class="o">()</span>
</span></span><span class="line"><span class="cl"> C:/workspace/go-practice/src/panic01/panic01.go:6 +0x1b
</span></span><span class="line"><span class="cl"><span class="nb">exit</span> status <span class="m">2</span>
</span></span></code></pre></div><p>となり,大域脱出させてアプリケーションを強制終了させているのが分かる。
この仕組みを <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Panic</a> と呼ぶ。</p>
<p><a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Panic</a> は意図的に発生させることもできる。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nf">f</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="s">"Panic!"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これを実行すると</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">C:<span class="se">\w</span>orkspace<span class="se">\g</span>o-practice<span class="se">\s</span>rc<span class="se">\p</span>anic02>go run panic02.go
</span></span><span class="line"><span class="cl">panic: Panic!
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">goroutine <span class="m">1</span> <span class="o">[</span>running<span class="o">]</span>:
</span></span><span class="line"><span class="cl">main.f<span class="o">()</span>
</span></span><span class="line"><span class="cl"> C:/workspace/go-practice/src/panic02/panic02.go:8 +0x6c
</span></span><span class="line"><span class="cl">main.main<span class="o">()</span>
</span></span><span class="line"><span class="cl"> C:/workspace/go-practice/src/panic02/panic02.go:4 +0x1b
</span></span><span class="line"><span class="cl"><span class="nb">exit</span> status <span class="m">2</span>
</span></span></code></pre></div><p>となる。</p>
<p>一方で, <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Panic</a> を <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Recover</a> することもできる<sup id="fnref:8"><a href="#fn:8" class="footnote-ref" role="doc-noteref">8</a></sup>。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">r</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="s">"Normal End."</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">r</span><span class="p">()</span> <span class="p">(</span><span class="nx">err</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">defer</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">rec</span> <span class="o">:=</span> <span class="nb">recover</span><span class="p">();</span> <span class="nx">rec</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="p">=</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Errorf</span><span class="p">(</span><span class="s">"Recovered from: %v"</span><span class="p">,</span> <span class="nx">rec</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="nf">f</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"> <span class="nx">err</span> <span class="p">=</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">f</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="s">"Panic!"</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>これを実行すると</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">C:<span class="se">\w</span>orkspace<span class="se">\g</span>o-practice<span class="se">\s</span>rc<span class="se">\p</span>anic03>go run panic03.go
</span></span><span class="line"><span class="cl">Recovered from: Panic!
</span></span></code></pre></div><p>となる。
<a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Panic</a> を <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Recover</a> で捕まえて通常の <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> として返しているのがお分かりだろうか。</p>
<p>一般的に <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Panic</a> はアプリケーション内で続行不可能な致命的エラーが発生した場合に投げられる。
例えばゼロ除算や領域外アクセスのようなエラーは <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Panic</a> が発生する前に回避するコードにすべきだ。
Allocation エラーのような回避不能かつアプリケーション続行不可能なエラーの場合は <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Panic</a> が投げられるのもやむを得ないが, <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Recover</a> することにほとんど意味はない。</p>
<p>例外的な使い方として <a href="https://golang.org/pkg/bytes/" title="bytes - The Go Programming Language"><code>bytes</code></a><code>.Buffer</code> では,メモリ確保で panic が発生した際に <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Recover</a> で捕まえ, <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> インスタンスを入れ替えて <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Panic</a> を投げ直している。
このような用途で <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Recover</a> を使うことはあり得る。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="c1">// makeSlice allocates a slice of size n. If the allocation fails, it panics
</span></span></span><span class="line"><span class="cl"><span class="c1">// with ErrTooLarge.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">func</span> <span class="nf">makeSlice</span><span class="p">(</span><span class="nx">n</span> <span class="kt">int</span><span class="p">)</span> <span class="p">[]</span><span class="kt">byte</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="c1">// If the make fails, give a known error.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span> <span class="k">defer</span> <span class="kd">func</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nb">recover</span><span class="p">()</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nb">panic</span><span class="p">(</span><span class="nx">ErrTooLarge</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"> <span class="p">}()</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nb">make</span><span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="nx">n</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>また再帰処理中に続行不能なエラーが発生した場合に <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Panic</a> を投げてトップレベルの関数に一気に復帰するような使い方をすることもある。
この場合,トップレベルの関数は(続行不可能なら)改めて <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Panic</a> を投げるか(処理続行できる根拠があるのなら)通常の <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> を返すことになる<sup id="fnref:9"><a href="#fn:9" class="footnote-ref" role="doc-noteref">9</a></sup>。</p>
<p>いずれにしろ,いわゆる「例外処理」的なハンドリングを <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Panic</a>/<a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Recover</a> で行うべきではない。</p>
<h2>【追記】 error の nil が nil にならない場合</h2>
<ul>
<li><a href="https://medium.com/@tucnak/why-go-is-a-poorly-designed-language-1cc04e5daf2#.ucutrogyz">Why Go is a poorly designed language — Medium</a> (<a href="http://postd.cc/why-go-is-a-poorly-designed-language/">日本語訳</a>)</li>
</ul>
<figure style='margin:0 auto;'>
<script src="https://gist.github.com/tucnak/eccdb53e7884084f5674.js"></script>
</figure>
<p>このコードの実行結果は “Hello, Mr. Pike!” を出力する。
このコードのポイントは <code>Generate()</code> 関数が <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> ではなく <code>*MagicError</code> 型を返している点にある。</p>
<p>実は <a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> を含む <a href="https://golang.org/doc/effective_go.html#interfaces_and_types" title="Effective Go - The Go Programming Language">interface</a> 型のインスタンスは値(への参照)と型情報をセットで保持っているため,上述のような形で nil を返しても受け取った側は「nil 状態を持つなにか」という評価になり,ただの <code><nil></code> にはならないのだ。</p>
<ul>
<li><a href="https://research.swtch.com/interfaces">research!rsc: Go Data Structures: Interfaces</a></li>
</ul>
<p>たとえば上のコードを以下のように書き換えると分かりやすいかもしれない。</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kn">package</span> <span class="nx">main</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kn">import</span> <span class="s">"fmt"</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">MagicError</span> <span class="kd">struct</span><span class="p">{}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">m</span> <span class="o">*</span><span class="nx">MagicError</span><span class="p">)</span> <span class="nf">Error</span><span class="p">()</span> <span class="kt">string</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Sprintf</span><span class="p">(</span><span class="s">"%#v"</span><span class="p">,</span> <span class="nx">m</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Generate</span><span class="p">()</span> <span class="o">*</span><span class="nx">MagicError</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">Test</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">return</span> <span class="nf">Generate</span><span class="p">()</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nf">Test</span><span class="p">();</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"> <span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nx">err</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"> <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>このコードを実行すると “<code>(*main.MagicError)(nil)</code>” と出力する。
<code>Generate()</code> 関数が返す nil がどのように機能しているか分かると思う。
「<a href="https://text.baldanders.info/golang/object-oriented-programming/">Go 言語における「オブジェクト」</a>」で解説するが, <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の型(<a href="https://go.dev/ref/spec#Properties_of_types_and_values" title="Properties of types and values">type</a>)は C++ や Java で言うところの class のように機能するため,このような動きになると思われる<sup id="fnref:10"><a href="#fn:10" class="footnote-ref" role="doc-noteref">10</a></sup>。</p>
<p>エラーハンドリングを行う際は結構ここがハマりどころになる。
ご注意を。</p>
<h2>ブックマーク</h2>
<ul>
<li><a href="http://www.kaoriya.net/blog/2014/04/17/">または私は如何にして例外するのを止めて golang を愛するようになったか — KaoriYa</a></li>
<li><a href="http://mattn.kaoriya.net/software/lang/go/20140416212413.htm">Big Sky :: golang で複数のエラーをハンドリングする方法</a></li>
<li><a href="http://dsas.blog.klab.org/archives/go-errors.html">DSAS開発者の部屋:Go ではエラーを文字列比較する?という話について</a></li>
<li><a href="http://qiita.com/ruiu/items/ff98ded599d97cf6646e">panicはともかくrecoverに使いどころはほとんどない - Qiita</a></li>
<li><a href="http://qiita.com/stsn/items/73714caf8458b1d973f2">Golang: nil Pointer Receiverの話 - Qiita</a></li>
<li><a href="http://qiita.com/tienlen/items/5f2bcfe06eb83830ee55">echoのAPIサーバ実装とエラーハンドリングの落とし穴 - Qiita</a></li>
<li><a href="http://deeeet.com/writing/2016/04/25/go-pkg-errors/">Golangのエラー処理とpkg/errors | SOTA</a> : 必見!</li>
<li><a href="https://medium.com/a-journey-with-go/go-multiple-errors-management-a67477628cf1">Go: Multiple Errors Management. Error management in Go is always prone… | by Vincent Blanchon | A Journey With Go | Sep, 2020 | Medium</a></li>
</ul>
<p><a href="https://text.baldanders.info/golang/bookmark/">Go 言語に関するブックマーク集はこちら</a>。</p>
<div class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1">
<p>ちなみに <a href="https://golang.org/" title="The Go Programming Language">Go 言語</a>の <code>goto</code> や ラベル付きの <code>break</code>, <code>continue</code> は<a href="https://golang.org/test/goto.go">飛び先に制約</a>があり,どこにでもジャンプできるわけではない。 <a href="#fnref:1" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:2">
<p>例外を備える言語でこれを緩和する仕様はいくつかある。例えば Java は 1.7 から <a href="http://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html">try-with-resources 構文</a>を導入した。もちろんこの構文を有効にするためには対象となるオブジェクトがこの構文に対応した作りになっていなければならない。 <a href="#fnref:2" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:3">
<p>いや,ファイル・オープンのエラーを無視したらダメです(笑) <a href="#fnref:3" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:4">
<p><a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Defer</a> 構文で指定された処理は <code>return</code> 時に起動することが保証される。したがって,エラー発生時にはその都度 <code>return</code> で抜けて問題ない。むしろ <code>goto</code> や <code>break</code> で強制的に処理を抜けるよりは処理を分割して <code>return</code> で安全に処理を抜ける方法がないか検討すべきである。なお <code>os.Exit()</code> などで強制終了した場合は, <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Defer</a> 構文で指定した処理は起動しないので注意。 <a href="#fnref:4" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:5">
<p>いわゆる「文芸的プログラミング」とは異なるけど。紛らわしくてゴメン。 <a href="#fnref:5" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:6">
<p>これからのコードは「文芸的」であることが必要条件だと思う。何故ならエンジニアにとって最も信頼できる「設計書」は(動いている)コードだからだ。コードをひとりで考えてひとりで書いてひとりで使ってひとりでメンテナンスするなら(本人さえ理解していれば)文芸的である必要はないかもしれない。が,実用的なコードでそんな状況はもはやありえない。コードにおいても暗黙知をできるだけ排除していくことが重要。 <a href="#fnref:6" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:7">
<p><a href="http://blog.golang.org/error-handling-and-go" title="Error handling and Go - The Go Blog">error</a> は組み込み型。組み込み型や組み込み関数(<code>append()</code> とか)は <a href="https://golang.org/pkg/builtin/" title="builtin - The Go Programming Language"><code>builtin</code></a> パッケージに定義が記述されているが,実際にこのパッケージを import して使うわけではない。 <a href="#fnref:7" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:8">
<p><a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Recover</a> は <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Defer</a> 構文とともに使用する。つまり <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Panic</a> 発生時でも <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Defer</a> 構文で予約された処理は実行される。 <a href="#fnref:8" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:9">
<p>これ以外にサーバ用途などでプロセスを落とせない場合に <a href="http://blog.golang.org/defer-panic-and-recover" title="Defer, Panic, and Recover - The Go Blog">Recover</a> で回避することもあるが,既に続行不可能な状態で無理やりプロセスを続行するのが正しい動きなのかどうかは疑問が残る。 <a href="#fnref:9" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
<li id="fn:10">
<p>それでもやっぱり nil は nil として扱ってほしいのだが。 <a href="#fnref:10" class="footnote-backref" role="doc-backlink">↩︎</a></p>
</li>
</ol>
</div>