【Go】静的型付け・型推論・関数・構造体・レシーバ・Go Modules・Packages・Imports

この記事は約12分で読めます。

Go言語を触り始めたので、備忘録まとめていく。

公式ドキュメントはこちら:https://go.dev/doc/
日本語の「A Tour of Go」も分かりやすい:https://go-tour-jp.appspot.com/list

静的型付け・型推論あり

:= 演算子

関数の中では、 var 宣言の代わりに、短い := の代入文を使い、暗黙的な型宣言ができる。

関数の外では、キーワードではじまる宣言( varfunc, など)が必要で、 := での暗黙的な宣言は利用できない。

型推論

明示的な型を指定せずに変数を宣言する場合( := や var = のいずれか)、変数の型は右側の変数から型推論される。

Go
i := 42           // int
f := 3.142        // float64
g := 0.867 + 0.5i // complex128

セロ値

変数に初期値を与えずに宣言すると、ゼロ値( zero value )が与えられる。

Go
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 型へ変換する。

Go
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 はフォーマットされた文字列を直接標準出力(通常はコンソール)に書き出す。
Go
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つ以上の引数が同じ型である場合には、最後の型を残して省略して記述できる。

Go
func add(x, y int) int {
	return x + y
}

関数は複数の戻り値を返すことができる

Go
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 メソッドを呼び出すことができる。

Go
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 変数にその変更が直接反映される。

Go
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
$ go mod init example.com/greetings

このコマンドは go.mod ファイルを生成し、モジュール名を example.com/greetings と設定する。

依存関係の管理:

パッケージの取得: 特定のバージョンのパッケージを取得してプロジェクトに追加する

Go
$ go get cloud.google.com/go/texttospeech/apiv1

依存関係の整理: go mod tidy 使われていない依存関係を削除し、必要な依存関係を追加する

Go
$ go mod tidy

Packages

Goのプログラムはパッケージで構成され、main パッケージから実行が開始される。

パッケージの宣言:

go mod init で作成されたモジュール名が example.com/greetings の場合、それを含むファイルの先頭には package greetings と宣言する。。これにより、そのファイル内の関数や型は greetings パッケージの一部として扱われる。

Go
package greetings

Imports:ファクタリング・エイリアス

パッケージをインポートする際には、単一またはファクタリングされたインポートステートメントを使用できる。

ファクタリングされたインポートステートメント:複数のパッケージを一括でインポートする

Go
import (
    "fmt"
    "math"
)

エイリアスの使用: エイリアスを使用してパッケージをインポートすることで、名前の衝突を避けたり、より短い名前で参照できる。このエイリアスを使ってパッケージの関数や型にアクセスできる。

下記例(Google CloudのText-to-Speech API)では、

  • texttospeech というエイリアスを cloud.google.com/go/texttospeech/apiv1 パッケージに設定している。これにより、パッケージ内の全ての公開された関数や型は texttospeech. をプレフィックスとして使用してアクセスできるようになる。
  • client, err := texttospeech.NewClient(ctx): NewClient 関数は texttospeech パッケージで定義されており、エイリアスを通じて呼び出している。
Go
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()
}

コメント

タイトルとURLをコピーしました