Go言語を触り始めたので、備忘録まとめていく。
公式ドキュメントはこちら:https://go.dev/doc/
日本語の「A Tour of Go」も分かりやすい:https://go-tour-jp.appspot.com/list
静的型付け・型推論あり
:= 演算子
関数の中では、 var
宣言の代わりに、短い :=
の代入文を使い、暗黙的な型宣言ができる。
関数の外では、キーワードではじまる宣言( var
, func
, など)が必要で、 :=
での暗黙的な宣言は利用できない。
型推論
明示的な型を指定せずに変数を宣言する場合( :=
や var =
のいずれか)、変数の型は右側の変数から型推論される。
i := 42 // int
f := 3.142 // float64
g := 0.867 + 0.5i // complex128
セロ値
変数に初期値を与えずに宣言すると、ゼロ値( zero value )が与えられる。
package main
import "fmt"
func main() {
var i int // 0
var f float64 // 0
var b bool // false
var s string // 空文字列
fmt.Printf("%v %v %v %qn", i, f, b, s) // 0 0 false ""
}
型変換
C言語とは異なり、Goでの型変換は明示的な変換が必要。
変数 v
、型 T
があった場合、 T(v)
は、変数 v
を T
型へ変換する。
i := 42
f := float64(i)
u := uint(f)
定数
定数( constant )は、 const
キーワードを使って変数と同じように宣言する。
定数は、文字(character)、文字列(string)、boolean、数値(numeric)のみで使える。
定数は :=
を使って宣言できない。
%v :フォーマット指定子
Go言語では fmt.Sprintf
関数(および関連する fmt
パッケージの関数)でフォーマット指定子 %v
を使用する。Go言語の fmt
パッケージは、”formatted I/O” または “formatted input/output” の略。
%v
フォーマット指定子は非常に汎用的で、プリミティブなデータタイプ(整数、浮動小数点数、ブール値、文字列など)から、複雑なデータ構造(配列、スライス、マップ、構造体など)まで、様々なタイプの値をデフォルトの形式で出力するのに使用される。
fmt.Sprintf
はフォーマットされた文字列を返し、それを変数に保存したり、他の関数への入力として使用することができる。fmt.Printf
はフォーマットされた文字列を直接標準出力(通常はコンソール)に書き出す。
package main
import "fmt"
type Person struct {
Name string
Age int
}
func main() {
person := Person{Name: "Alice", Age: 30}
fmt.Printf("Person details: %vn", person) // 出力:Person details: {Alice 30}
scores := []int{92, 85, 88} // int型の配列を宣言
fmt.Printf("Scores: %vn", scores) // 出力:Scores: [92 85 88]
greeting := fmt.Sprintf("Hi, %v. Welcome!", person.Name)
fmt.Println(greeting) // 出力:"Hi, Alice. Welcome!"
}
関数
関数はfuncキーワードで始まり、引数と戻り値の型が必要(静的型付け)
変数名の「後ろ」に型名を書く
関数名・引数・戻り値の順に記載
関数の2つ以上の引数が同じ型である場合には、最後の型を残して省略して記述できる。
func add(x, y int) int {
return x + y
}
関数は複数の戻り値を返すことができる
package main
import "fmt"
func swap(x, y string) (string, string) {
return y, x
}
func main() {
a, b := swap("hello", "world")
fmt.Println(a, b)
}
大文字(Public関数)・小文字(Private関数)
Go言語では、関数名や変数名が大文字で始まる場合、その関数や変数は「エクスポートされた」(Exported)と見なされ、他のパッケージからアクセス可能になる(Public関数)。これは、その名前がパッケージの外部に「公開」されていることを意味する。
逆に、小文字で始まる名前は、定義されたパッケージ内でのみアクセス可能(Private関数)。つまり、他のパッケージから利用したい関数や変数には大文字で始める必要がある。
構造体(Struct)
Go 言語における struct
(構造体)は、フィールド(プロパティや属性とも言える)の集まりで、他の多くのプログラミング言語のクラスに類似した役割を果たす。ただし、Go の構造体はクラスと異なり、メソッドは構造体の定義の外部で定義され、構造体とメソッドは「レシーバ」を介して関連付けられる。
structのフィールドは、ドット( .
)を用いてアクセスする。
レシーバを介したメソッドとの関連付け
Go ではメソッドは構造体と直接的には結びついていないが、構造体のインスタンス(またはポインタ)をメソッドの「レシーバ」として使用することで、クラスのような振る舞いを実現できる。
下記例では、Greet
メソッドが Person
型のレシーバ p
を使って定義されている。これにより、Person
型の各インスタンスに対して Greet
メソッドを呼び出すことができる。
package main
import "fmt"
// Person 構造体の定義
type Person struct {
Name string
Age int
}
// Person 構造体に対するメソッドの定義
func (p Person) Greet() {
fmt.Println("Hello, my name is", p.Name)
}
func main() {
// Person 構造体のインスタンスを作成
p := Person{Name: "Alice", Age: 30}
// ドット(.)を用いてメソッドにアクセス
p.Greet() // 出力: Hello, my name is Alice
// ドット(.)を用いてフィールドにアクセス
fmt.Println(p.Name) // 出力: Alice
fmt.Println(p.Age) // 出力: 30
}
ポインタレシーバ
ポインタレシーバーを使うと、そのメソッドがその型のインスタンスのポインタを通じて呼ばれることを意味する。
ポインタレシーバーを使用すると、メソッドはオブジェクトのポインター、つまりそのオブジェクトのメモリアドレスを受け取る。これにより、メソッド内での変更が元のオブジェクトに直接反映されるため、値渡しではなくアドレス渡しになる。
Resize
はポインタレシーバー (*Rectangle
) を使用しているため、メソッド内で Rectangle
のフィールドを変更すると、main
関数内の rect
変数にその変更が直接反映される。
package main
import "fmt"
type Rectangle struct {
Width, Height int
}
// ポインタレシーバーを使用してメソッドを定義
// このメソッドはRectangleのインスタンスを変更可能
func (r *Rectangle) Resize(newWidth, newHeight int) {
r.Width = newWidth
r.Height = newHeight
}
func (r *Rectangle) Area() int {
return r.Width * r.Height
}
func main() {
rect := Rectangle{Width: 10, Height: 5}
fmt.Println("Original dimensions:", rect) //出力: Original dimensions: {10 5}
fmt.Println("Area:", rect.Area()) // 出力: Area: 50
// メソッドを呼び出して、rectのサイズを変更
rect.Resize(20, 10)
fmt.Println("Resized dimensions:", rect) //出力: Resized dimensions: {20 10}
fmt.Println("Area:", rect.Area()) // 出力: Area: 200
}
ポインタレシーバーの利点:
- 変更可能性: ポインタを通じて構造体のフィールドを変更すると、その変更は元のインスタンスにも反映されます。値レシーバーを使うと、メソッドに渡されたのは構造体のコピーなので、その変更はローカルスコープに留まる。
- パフォーマンス: 大きな構造体を扱う場合、そのコピーを作成するのはコストがかかる。ポインタを使うと、データをコピーすることなく直接操作できるため、パフォーマンスが向上する
ポインタレシーバーを使う時:
- オブジェクトの状態を変更するメソッドを定義する場合。
- オブジェクトが大きい場合や、頻繁に変更される場合で、パフォーマンスを考慮する必要がある場合。
Go Modules と go.mod ファイル
Goモジュールは、プロジェクトの依存関係を管理するシステム。
go.mod
ファイルはその中心となるファイルで、モジュールの名前とその依存する他のモジュールのリストを含む。
モジュールの初期化:
$ go mod init example.com/greetings
このコマンドは go.mod
ファイルを生成し、モジュール名を example.com/greetings
と設定する。
依存関係の管理:
パッケージの取得: 特定のバージョンのパッケージを取得してプロジェクトに追加する
$ go get cloud.google.com/go/texttospeech/apiv1
依存関係の整理: go mod tidy 使われていない依存関係を削除し、必要な依存関係を追加する
$ go mod tidy
Packages
Goのプログラムはパッケージで構成され、main
パッケージから実行が開始される。
パッケージの宣言:
go mod init
で作成されたモジュール名が example.com/greetings
の場合、それを含むファイルの先頭には package greetings
と宣言する。。これにより、そのファイル内の関数や型は greetings
パッケージの一部として扱われる。
package greetings
Imports:ファクタリング・エイリアス
パッケージをインポートする際には、単一またはファクタリングされたインポートステートメントを使用できる。
ファクタリングされたインポートステートメント:複数のパッケージを一括でインポートする
import (
"fmt"
"math"
)
エイリアスの使用: エイリアスを使用してパッケージをインポートすることで、名前の衝突を避けたり、より短い名前で参照できる。このエイリアスを使ってパッケージの関数や型にアクセスできる。
下記例(Google CloudのText-to-Speech API)では、
texttospeech
というエイリアスをcloud.google.com/go/texttospeech/apiv1
パッケージに設定している。これにより、パッケージ内の全ての公開された関数や型はtexttospeech.
をプレフィックスとして使用してアクセスできるようになる。client, err := texttospeech.NewClient(ctx)
:NewClient
関数はtexttospeech
パッケージで定義されており、エイリアスを通じて呼び出している。
package main
import (
"context"
"fmt"
"log"
texttospeech "cloud.google.com/go/texttospeech/apiv1" // エイリアスを使ったインポート
)
func main() {
ctx := context.Background()
// クライアントの作成
client, err := texttospeech.NewClient(ctx)
if err != nil {
log.Fatalf("Failed to create client: %v", err)
}
defer client.Close()
}
コメント