Skip to main content

Golang

日本のGophers代表? 解説記事がある
メルカリ(プログラミング言語Go完全入門)

コードの長さ

Goは冗長な書き方となる。しかしそれがデメリットとはならない。
初心者でも導入がしやすいと裏付けられる。
Goよりもコードの密度が高く、より短く書けるプログラミング言語はある。
RubyやPythonであればコードの行数は半分から1/3になる。

Go実行速度

Goは実行速度では遅いが、コンパイルが圧倒的に早くネットワークなどのI/O速度が支配的なネットワークアプリケーションやコンテナーで動作するアプリケーションであれば十分な速度が出せる。


Goのモジュール管理

参考URL

$ go env
# 1.16からGO111MODULE未指定時の規定値が`on`になった(そのためモジュールモードでやっている)
GO111MODULE=""

GOPATH モードとモジュール対応モード

Go のモジュール管理
バージョン 1.11 以降からGoツールーチェーンは以下の2つのモードのどちらかで動作する。

  • GOPATHモード(GOPATH mode)
    • バージョン 1.10 までのモード。標準ライブラリを除くすべてのパッケージのコード管理とビルドを環境変数GOPATHで指定されたディレクトリ下で行う。パッケージの管理はリポジトリの最新リビジョンのみが対象となる
  • モジュール対応モード(module-aware mode)
    • 標準ライブラリを除くすべてのパッケージをモジュールとして管理する。コード管理とビルドは任意のディレクトリで可能で、モジュールはリポジトリのバージョンタグまたはリビジョン毎に管理される

モジュールとは

モジュール対応モードでは、標準ライブラリを除くパッケージをモジュールとして管理する。
パッケージが単一のディレクトリを指すのに対し、モジュールは go.mod ファルのあるディレクトリ以下の(go.modは含まない)すべてのパッケージがモジュールの配下となる。

go.sum

SHA-256 チェックサム

SHA-256 チェックサムは、データの整合性を確認するために使用されるハッシュ関数の出力。
SHA-256は「Secure Hash Algorithm 256-bit」という意味で、256ビット(32バイト)のハッシュ値を生成する。
このハッシュ値は、ファイルやデータの内容が変更されていないかどうかを確認するのに役立つ。

SHA-256 チェックサムの仕組み

  • ハッシュ関数: SHA-256はハッシュ関数の一種で、任意の長さのデータを取り、固定長のハッシュ値(この場合は256ビット)を出力する
  • 一意性: 同じ入力データに対しては常に同じハッシュ値が生成されます。しかし、わずかでも入力データが変わると、ハッシュ値はまったく異なるものになります。
  • 逆変換不可能: ハッシュ値から元のデータを復元することは計算上非実用的です。

go.sum ファイルにおける SHA-256 チェックサムの役割

Go言語のモジュールシステムでは、go.sum ファイルに各依存関係のSHA-256チェックサムを記録します。これは以下の理由で重要です:

  • 整合性の検証: go.sum ファイルに記録されたハッシュ値を使用して、依存関係のファイルが変更されていないことを確認します。これにより、ソースコードが意図せず変更されたり、悪意のあるコードが注入されたりするリスクを軽減できます。
  • セキュリティ: チェックサムによって、依存関係のファイルが配信元からダウンロードされた後に変更されていないことが保証されます。これはとくにオープンソースの依存関係を使用する場合に重要です。
  • 再現可能なビルド: go.sum に記録されたチェックサムにより、プロジェクトが依存するモジュールの正確なバージョンを保証できます。これにより、異なる環境で同じビルド結果を再現できるようになります。 要するに、go.sum ファイルに記録されたSHA-256チェックサムは、Goプロジェクトの依存関係のセキュリティと整合性を保証するための重要な機構です。これにより、開発者は依存関係が安全で、変更されていないことに確信を持ってコードを書くことができます。

go コマンド

パッケージをインストールするとgo.modおよびgo.sumに情報が反映される。 go getやgo mod tidyによってインストールされたパッケージは$GOPATH/pkg/modに保存される。

go get [パッケージ名] # 指定したパッケージのインストール
go get # importに記載されたパッケージのインストール
go mod tidy # importに記載されたパッケージのインストール。不要なパッケージの削除

パッケージのインストールが必要なGoファイルを実行する場合、Go 1.16では go mod tidy or go getgo build という手順。

go get オプション

参考URL

go mod tidy, go get, go install違い

go mod tidygo installgo get は、Goプログラミングにおける依存関係の管理やパッケージの取得に使用されるコマンド

  1. go mod tidy:

    • go mod tidy コマンドは、Goモジュールの依存関係を整理するためのコマンドです。
    • プロジェクトの go.mod ファイルに記載されている依存関係を解決し、不要な依存関係を削除します。
    • 使用されていないモジュールがある場合は削除し、モジュールのバージョンを最新に更新します。
    • プロジェクトの依存関係を最新の状態に保つことができます。
  2. go install:

    • go install コマンドは、Goプログラムをビルドして実行可能なバイナリファイルを作成し、指定されたパッケージをインストールします。
    • ソースコードをビルドして実行ファイルを生成し、その実行ファイルを $GOPATH/bin に配置します。
    • インストールされた実行ファイルは、コマンドラインから直接実行できる。
    • go install を使用することで、自作のパッケージやツールを利用する他のプロジェクトから利用できる。
  3. go get:

    • go get コマンドは、指定したパッケージを取得し、依存関係を解決してインストールするためのコマンドです。
    • go get を使用することで、公開されているパッケージやモジュールを取得し、ローカルにダウンロードします。
    • インストールされたパッケージは、他のプロジェクトで import 文を使用して利用できる。

メリットとしては、以下のような特徴があります:

  • go mod tidy:

    • 不要な依存関係を削除してプロジェクトのモジュールを整理できます。
    • モジュールのバージョンを最新の状態に保つことで、セキュリティの問題やバグの修正などの改善を取り入れることができます。
  • go install:

    • 自作のパッケージやツールを他のプロジェクトから利用できるようになります。
    • ビルドされた実行

ファイルを容易に実行できます。

  • go get:
    • 公開されているパッケージやモジュールを簡単に取得できます。
    • 依存関係の解決やインストール作業が自動的に行われます。

これらのコマンドを適切に使用することで、Goプロジェクトの依存関係を管理し、必要なパッケージやモジュールを取得して開発を行うことができます。

Go モジュール・パッケージ

Goのアプリケーションとライブラリは、それぞれモジュールと呼ばれる塊になっている。 1つのフォルダーが1つのモジュールとなる。

実行ファイルorライブラリでもまず次のコマンドを叩いてプロジェクトの中心となるファイル go.mod を作る

最後に付与するのはパッケージ名 $ go mod init hello

パッケージ 1つのファイルを複数に分割したもの。1ディレクトリ=1パッケージ

モジュール go.modファイルのあるディレクトリ以下のすべてのパッケージ(go.modは含まれない) 1レポジトリ=1モジュール

Go モジュール・パッケージをGitHubで外部に公開する場合

github.con/アカウント名/リポジトリ名/ を使う

### サンプルコードなどであれば以下のような感じでOK
$ go mod init go-example

### 公開前提の場合はモジュールのパスを指定する
$ go mod init github.com/naohito-T/go-example

Go エントリーポイント

mainパッケージは特別なパッケージ。 mainパッケージのmain関数からプログラムが開始される。

Go CLI

go fmt:コードをGo言語の標準フォーマットに変換するツール
go vet:間違えやすいコードを指摘するツール
golint:スタイルの問題を指摘するツール
godoc:コードからAPIドキュメントを作るツール

Go 歴史

昔(Go1.11以前)は $GOPATH/src 配下でしか開発できなかった。 その後Go modulesの導入により $GOPATH/src にプロジェクトを置かなければならないという制約からは解放されたので、各プロジェクト毎に GOPATH を指定するみたいなことがいらなくなったという経緯 そのため、こちらも現在はデフォルトから変える必要性はないです。

現在の最新歴史。これを見れば大体わかる 参考URL

Googleが開発したプログラミング言語 2009年11月に最初のバージョンをオープンソースで公開 2012年3月に正式バージョンであるGo1.0を公開 2022年5月現在の最新バージョンはGo1.18 半年毎のペースでバージョンアップ

モジュール対応モード

標準ライブラリを除くすべてのパッケージをモジュールとして管理する。 コード管理とビルドは任意のディレクトリで可能。Go1.17からは常にコレ。

モジュールとは? パッケージ: 1つのファイルを複数に分割したもの。1ディレクトリ=1パッケージ モジュール: go.modファイルのあるディレクトリ以下のすべてのパッケージ(go.modは含まれない) →1レポジトリ=1モジュール


Go 利用例

Go言語は比較的新しいプログラミング言語でありながら、世界的な動画配信サービスであるYouTubeのサーバー構築や有名Webアプリの開発などにも使用されており、人気の高い言語として世界中で愛されています。


Go 特徴

強力でシンプルな言語設計と文法 並行プログラミング 豊富な標準ライブラリ群 周辺ツールの充実 シングルバイナリ・クロスコンパイル

Go特徴

Go言語はC言語やJavaなどと同じく静的型付け言語でありながら、PythonやJavaScriptなどの動的型付け言語のような特徴を持つ。 Go言語はマルチプラットフォームに対応しており、Windows/macOS/Linux/Android/iOSなどの幅広いOSに対応している(クロスプラットフォームへコンパイルできる) そのため、Webアプリケーションだけでなく、スマートフォンで動作するアプリの開発も可能。

Go言語のフレームワークでもあるGobotを使用することで、ドローンやロボットなどの高度な組込みシステムの開発も可能。 Gobotにはネットワーク上のデバイスや複数のデバイス間で相互に通信できる機能があり、外出先からデバイスを操作したりセンサーが反応したときに他のデバイスと機能を連動させたりできる。

Go言語は、近年注目の集まるクラウドやコンテナーー技術、マイクロサービスなどの最新IT技術と親和性が高く、将来性のある言語

メモリ管理の手間が少ないというメリットがありますがGC(ガベレージコレクション)のアルゴリズムがSTOP THE WORLDを採用しているため、ミッションクリティカルな場面やメモリが貧弱な環境には適さない。

クロスコンパイル対応をするには

環境変数を設定する必要がある。

# Windows(32ビット)向けにコンパイル
$ GOOS=windows GOARCH=386 go build
# Linux(64ビット)向けにコンパイル
$ GOOS=linux GOARCH=amd64 go build

Go ディレクトリ構成

参考URL リファレンス

Go test

golangにはgo testツールなる便利なものがある。

制約

  1. ファイル名の最後を_test.goとしなければいけない。
  2. testingをimportする必要
  3. テストロジックの関数は Test** と始める。TestA or TestAbcdefgでOK
  4. 引数には*testing.Tを入れる。

Goでのテスト結果 Goはテストのアサーションを提供していない。テストが失敗した通知に関しては、テストが失敗したことを開発者自ら実装する必要がある。 失敗したことを示すには T.Error (T.Errorf)T.Fatal (T.Fatalf) を用いることができる。

T.Fatal を用いると T.Fatal実行された以降のテストは呼び出されずに終了する。 テストが失敗したことを示すには T.Error を使い、テストの初期化など、処理が失敗するとその後のテストが無意味になる場合は T.Fatal を用いると良い。 以下のように t.Fatalf を用いた場合は、それ以降は呼び出されないが、deferT.Cleanup といった後処理は呼び出されます。

Goにおける静的解析

参考URL

Goはソースコードを実行せずに解析する静的解析の機能を提供するgoパッケージが標準ライブラリとして用意されている。 そのため静的解析を用いたコードフォーマッタやLinterなどが作りやすい言語。

Go 仕組み

コンパイル

Go 変数

Goの関数や変数は、大文字からスタートするとほかのパッケージから参照できるようになる。 ※小文字だと参照できない Go言語では一度宣言した変数を使わなかったら怒られる。そのため関数の戻り値を使用しない場合は _(アンスコ) などを使用し使用しない宣言をする。

変数の宣言と代入を同時に行う場合には := 演算子がある。 これは関数の中でしか使えない

変数命名慣例 Goの変数名は長い名前よりも短い名前が好まれる 変数のスコープが限定

変数名はどんな時にも短ければ良いわけではない。 グローバル変数にはわかりやすい名前を、ローカル変数には短い名前をこの考え方は一般的。

Go 定数

Goのconstはコンパイル時に決定できないものはconstにできない(たとえばnewとか)

パッケージ名

パッケージ名は小文字が基本 internalという名前だけはモジュール外から読めないパッケージとなる。

テストへ用いるパッケージに パッケージ名 + _test という名前を用いることがある。 _testがついたパッケージからは公開されているパッケージの型、関数のみしか参照できない参照できないためExampleテストややブラックボックステストのような用途で使用される。

これもやめた方がいい(変数名および関数名はパッケージ名も含み考える)

// http.Serverであって、http.HTTPServerではない。

Go データ型

Goの文法ではリテラルの状態では型を持っていない。変数に代入されると型が決まる Goの場合は暗黙の型変換がなく、明示的に型変換をしなければ他の変数に代入したりできない

nil 無効な参照先を表す値

ポインター型 ポインター型の初期値はnilで、nilの参照先を取り出そうとするとプログラムがパニックを起こして終了する。 リテラルはポインターが取れない。一度変数に格納するなどが必要。

参照型 関数・スライス・マップなどは*をつけない場合もポインターと同等の動きをする。 これらは参照型と呼ばれる。 使う場合はそのままでリファレンスをせずに使われるが、コピーなどは参照のみがコピーされるされる。

スライス 多くのプログラミング言語にはリストや配列といったデータ構造はある。 →同じ型のデータが複数並んでいるデータ構造になる Goにも配列はあるが、配列ではなくスライスの方を多用する

配列やスライスは複合型と呼ばれる。

そしてスライスは配列を参照する窓のようなデータ構造 他の言語ではビューと呼ばれることがあるある。

Go 制御構文

Goが持つ制御構文は if / for / switch

if 他の言語にはたいてい falsy の概念があるが、Goにはif文の暗黙変換がない。
boolean値のみしかif文で使用できない

for スライスの場合はインデックスと値
マップの場合はキーと値

switch if ... else ifを羅列して書くのを簡単にしてくれるのがswitch

Go 関数

無名関数もある。 名前付き関数はパッケージレベルでしか作れないが、無名関数は関数の中でも使える。

defer

deferは関数のブロックを抜けるタイミングで処理の予約ができる。 言語が変わっても後片付けは準備と同じ場所で予約するのが一番確実の原則は一緒

エラー処理

関数の返り値にerrがある場合は err と呼ばれる変数に代入し、その次の行でerrがnilではない(何か値が入っている) これがエラー処理の基本。例外処理はない。

昔から変わらないGoの慣習は以下 失敗する可能性のある関数の末尾をerror型とする

Go 構造体

参考URL

構造体は中にメソッドを定義できない

type Book struct {
Title string
Author string
Publisher string
ReleasedAt time.Time
}

// インスタンス作成(フィールドはすべてゼロ値に初期化)
var b Book

// フィールドを初期化しながらインスタンス作成
b2 := Book{
Title: "sample"
}

// フィールドを初期化しながらインスタンス作成(変数にはポインター)
b3 := &Book{
Title: "sample"
}

Goではアプリケーションの実装では構造体をよく利用する。 jsonタグを定義しておくと、この定義にしたがって構造体のフィールドをJSONに書き出したり、JSONの情報フィールドにマッピングできる

Go Optional

// 引数を示す構造体
// フィールドが未指定だったのか,ゼロ値が指定されたのかを
// 区別するため,型はポインタにする⇨*string
// 引数を示す構造体
// フィールドが未指定だったのか,ゼロ値が指定されたのかを
// 区別するため,型はポインタにする
type GreetOpts struct {
GreetingWord *string
}

// オプショナルパラメータを構造体で受け取る
func Greet(name string, opts *GreetOpts) {
greetingWord := "Hello"
if opts.GreetingWord != nil {
// 引数がnilだったら未指定なのでデフォルト値で埋める
greetingWord = *opts.GreetingWord
}
fmt.Printf("%s, %s!\n", greetingWord, name)
}

func main() {
Greet("gopher", &GreetOpts{}) // Hello, gopher!

word := "Hey"
Greet("gopher", &GreetOpts{GreetingWord: &word}) // Hey, gopher!
}

Go 型

参考URL

Greet("gopher", &GreetOpts{}) // &nnn &はポインタで渡す(参照))

Go import

参考URL

period importとblank importという別のimportがある。

period(.) import あまりオススメはされていない

interface型

参考URL interface型 -> どんな型も格納できる特殊な型・型チェックや型変換などに使える interface -> type structの下に関数群を紐付ける書き方。オブジェクト指向でないGoにおいてclassに近しいことができる(ただし継承などはできない) 2つは本質には同じもので実装するためのメソッド(中身)のない型の使い方に過ぎない、が個人的には分けて考えたほうが考えやすかった

class

他の言語でのクラス・メンバー変数に当たる部分はGoでは構造体・フィールドとして存在する


GOROOT

GoのSDKの場所を定義している

GOPATH

ワークスペースの場所を定義している。 グローバルインストールしたパッケージなどはすべてここに保管されている →$HOME/go/bin ※Macなら$HOME/goに自分で設定してなくても勝手に設定されてるはず(Go1.8以降)


Editor

vscodeで全然いい

vscode拡張機能

Goをインストールすればいいだろう

Go Language Server Protocol

参考URL

現状ではgoplsができており、ジャンプなどもすぐにできるようになる。 ※ただし、go.mod があるディレクトリ内でファイルを開かないといけない

go getと installの違い

参考URL 参考URL

go1.17から go get を使わなくなったが go get が完全に使えなくなるわけではない(警告はでる)

go1.16から以下の通りとなる。 go install バイナリのビルドとインストールのため go get go.mod編集のための go get

go buildgo test で自動的に go.mod が更新されることはなくなった。 go.modの編集は go get or go mod tidy あるいは手作業で行います。

モジュール内で管理したいパッケージについてはこれまで通り go get go installモジュール外で使いたいパッケージのインストールに使用する

Go とは

2009年11月にGoogleから発表されたプログラミング言語で、ソースコードをコンパイルして実行するコンパイル言語。 Goは得に言語のコアをシンプルに、なにかを実現するときはそのシンプルな機能を組み合わせて実現するというコンセプト

参考URL(コードの考え方でもいい)

GOPATHとは

このディレクトリは、GoのソースコードやGoの実行可能ファイル、並びにコンパイル済みのパッケージファイルを保存する為に使用します。そのためこのディレクトリには3つのサブディレクトリが存在します:src、bin、pkgです。

$HOME/.go が固定になったとのこと。go moduleになってからGOPATHの設定は不要になった。

GO PATHの構成

  • bin ビルド済の実行可能ファイル群

  • pkg ビルド済のオブジェクトファイル群(拡張子は".a") モジュールモードのときにダウンロードされるソースコード群

  • src GOPATHモードのときにダウンロードされるソースコード群 開発するソースコードを置くディレクトリ(ここで開発することが必須ではない)

goで開発するときは基本的にはGOPATHを意識して開発することになるのでとりあえずGOPATHを設定しておく。

Go のパッケージ管理方法

参考URL

Go言語のパッケージ管理方法にはGOPATHモードとmodule-awareモードの2種類が存在する。

  • GOPATHモードについて
  • module-awareモード

Go: DepからGo Modulesへの以降

Goのモジュール管理のdepはもう古い Go modulesはGo 1.13から正式に導入されたGo言語公式の依存パッケージ管理ツール Go modules以前は、depが依存パッケージ管理のツールとしてはデファクトだった。


ここからは昨今のGo moduleでの環境のsetupについて

Go moduleとは

参考URL

Go moduleモードでは、GOPATH配下にプロジェクトを置かなければならないという制約からは解放された。 なので実質、GOPATHはどこをさしても構わないし、設定をされていなくてもUser/goというデフォルトの場所が決まっているため開発が楽。

  • モジュールとパッケージとは モジュール = パッケージをひとつまたは複数のサブパッケージを取りまとめたカタマリ パッケージ = フォルダー単位で単一ファイルまたは複数ファイルのかたまり サブパッケージ = サブフォルダーにおくだけで扱いはパッケージと同等

あるフォルダーにてモジュールだと宣言するとそのフォルダー以下がモジュールになる。 宣言方法は以下の通り

$ go mod init <モジュール名>

こうするとカレントフォルダーにgo.modファイルが作成され、このカレントフォルダー以下がまるっと「モジュール」の扱いになります。

パッケージ名とはパッケージに置かれるGoコードの先頭に宣言される名称で、これは極力そのパッケージのフォルダー名と合致させることが推奨されます

相対パスインポートを見たらそのコードは古い可能性が高いので参考にしない様に。

Go module始め方

$ go mod init [import name]

  • go mod initの名前は?

書くべきなのはimportpath 一般的な用途ではリポジトリルートのURLを「importpath」としてgo mod initの引数に使うのが良いでしょう。 go mod init sampleについてはリポジトリと紐づけない一時的なものとして利用することはできます。こうして作られたモジュールは公開する条件を満たせないので例えリポジトリに置いて公開しても第三者がgo-getすることはできないモジュールになります。もちろんこの形での公開は推奨はされません。(手元で書き捨てる用途とか学習用途とお考え下さい)

go.mod と go.sum

参考URL

  • go.mod 主にモジュールのインポートパスとバージョン情報を書いておくためのファイルで、いくつかのディレクティブを使って、アプリケーションがどのような依存関係を持っているか記述しておく

go mod tidy等を実行するとこのファイルを元に依存先を取得し次項で解説するgo.sumを生成する。

  • go.sum 直接・間接問わずに依存先モジュールのハッシュを記録するためのファイル

ライブラリのインポート

他のパッケージで公開されている機能を利用するには import文 を使用 Goでは中央集権的なライブラリのリポジトリはない(npmみたいな) github.comやgitlab.comなどのリポジトリや独自ドメインで運用しているサーバなどを使ってライブラリの配布が行われる

そのためパッケージの識別子はそのライブラリの情報が取得可能なURL

go プロジェクト version固定

go get

  • example $ go get github.com/labstack/echo/v4

  • option

-u : 新しいマイナーリリースまたはパッチリリースが利用可能な場合にインストール -d : パッケージのダウンロードのみ、インストールなし(PCに組み込まないということ) -tオプション : 指定されたパッケージのテストに必要なパッケージも一緒にダウンロードしてくれる。 -vオプション :実行しているコマンドを表示する。 -fixオプション : 依存関係を修正。ダウンロードしたパッケージを修正。修正してからgetをしてくれる。 -insecureオプション : HTTP等の安全性が確保されていない方式を使いリポジトリから取得するためのコマンド。危険が伴うため、使用はオススメしません。


Tips Go

go admin

GoでのORM

Goにもいくつかの代表的なORMライブラリがありますが、標準ライブラリのみを使って実装することも好まれがち

ただ、 データベースとやり取りする部分はWEBアプリケーションでは頻繁に登場するにもかかわらず、似たようなコードを都度実装することになりがち。 ORMライブラリを使うことで、そのようないわゆるボイラープレートコードを減らし、本質的な実装に注力できる。

Go ORM種類 Go並列処理 とほほのGo言語入門 GoとDockerハンズオン(最高に良い)

Goを学ぶ時につまずきやすいポイント

参考URL


GoDoc

GoDoc

godocとgo docは同じドキュメントツールだが別のツール

Go製のライブラリは標準ライブラリもサードパーティのライブラリもすべてhttps://pkg.go.devで公開される。 現在のGoは go mod を使ってパッケージの取得をする。 パッケージのソースコードはGitHubやGitLabにあるが、go mod はproxy.golang.org経由でこのソースを取得するがこのタイミングでドキュメントも取り込まれる。 ライブラリは一元管理されていないがドキュメントが一元化されるのはこのような仕組みによるもの。

チョットできるGoプログラマーになるための詳解GoDoc

godoc コマンド

Goには godoc コマンドがある。 このコマンドのおかけでgolang.orgのパッケージドキュメントをオフラインで閲覧できる。

# port指定
$ godoc -http ":3000"

# localhost:6060 デフォルト
$ godoc

go doc コマンド

Go DB

Goでの組み込みDB関連パッケージ Goの構造体をデータベースのスキーマ、あるいはテーブルにマッピングできるいわゆるORMは標準ライブラリとして提供されていない。 便利なDB関連サードパーティもdatabase/sqlパッケージのラッパーで最終的にはdatabase/sqlパッケージを使う。

database/sql Goのアプリケーションから呼び出すためのAPI(DBドライバーの実装に依存しない汎用的なAPI) コネクションプーリングや並行処理の制御といった複雑な処理を吸収してくれる 標準

database/sql/driver ドライバー向けのパッケージ アプリケーションのコードからdriverパッケージを利用することはない。

Go言語でメソッドのレシーバータイプ(ポインタ vs 値)を選択する際のガイドライン

  • レシーバーに渡す構造体がメモリ上で大きい領域を占めている場合にポインタレシーバーを使う
  • 値レシーバーでコピーを渡す場合、その大きなものをメモリ上の別の領域にコピーしてから、そのコピーしたものを渡す必要がある
  • 一方ポインタレシーバーではコピーなどせず、そのデータのメモリ上でのアドレスを渡すだけでいいのでメモリにやさしい

以下は、Go言語でメソッドのレシーバータイプ(ポインタvs値)を選択する際のガイドラインを考慮した例です。この例では、いくつかの異なるシナリオを示し、それぞれの場合に適したレシーバータイプを使用しています。

例1: レシーバが大きな構造体の場合(ポインタを使用)

package main

import (
"fmt"
)

// LargeStruct は多くのフィールドを持つ大きな構造体です。
type LargeStruct struct {
Data [1024]int
}

// Update は LargeStruct の内容を更新するメソッドです。大きな構造体なので、ポインタレシーバを使用します。
func (l *LargeStruct) Update(index int, value int) {
l.Data[index] = value
}

func main() {
l := LargeStruct{}
l.Update(10, 100)
fmt.Println(l.Data[10]) // 出力: 100
}

例2: レシーバーが小さな構造体で、値の変更がない場合(値レシーバーを使用)

package main

import (
"fmt"
"time"
)

// TimeWrapper は time.Time を包含する小さな構造体です。
type TimeWrapper struct {
t time.Time
}

// Day は TimeWrapper の日を返します。変更を加えないので、値レシーバを使用します。
func (tw TimeWrapper) Day() int {
return tw.t.Day()
}

func main() {
tw := TimeWrapper{t: time.Now()}
fmt.Println(tw.Day()) // 現在の日にちを出力
}

例3: レシーバが sync.Mutex または同期を必要とするフィールドを含む場合(ポインタを使用)

package main

import (
"fmt"
"sync"
)

// SafeCounter は安全にカウントアップするための構造体です。sync.Mutex を含むのでポインタレシーバを使用します。
type SafeCounter struct {
mu sync.Mutex
count int
}

// Increment はカウンタを1つ増やします。
func (s *SafeCounter) Increment() {
s.mu.Lock()
s.count++
s.mu.Unlock()
}

// Value は現在のカウンタの値を返します。
func (s *SafeCounter) Value() int {
s.mu.Lock()
defer s.mu.Unlock()
return s.count
}

func main() {
s := SafeCounter{}
s.Increment()
fmt.Println(s.Value()) // 出力: 1
}

これらの例は、レシーバを選択する際の一般的なガイドラインを反映しています。構造体が大きい、または変更可能な状態を持つ場合はポインタレシーバを使用し、小さくて不変のデータの場合は値レシーバを使用します。また、同期が必要な場合や、内部状態が外部から変更される可能性がある場合もポインタレシーバが推奨されます。

Goには「大域脱出」を例外処理としてない

throw などがないということ(厳密にはpanicがある)
そのため関数の呼び出し元が例外を正常な状態に戻すべきという思想があり、そのため。 「例外」がないからGo言語はイケてないとかって言ってるヤツが本当にイケてない件

Goに三項演算子が採用されない理由

GoもPythonもif-elseが文であり、式として扱えない方針を採った。
Goに三項演算子が採用されない理由