🪬

Go初心者が1日でGoを一周した内容を雑にまとめる part1

2024/12/27に公開

はじめに

こんにちは。今回は、モダンなプログラミング言語であるGo(ゴー)言語について紹介する。Go言語は、そのシンプルな文法高速なコンパイル速度、そして強力な並行処理機能を特長としている。

なぜ今、Go言語を学ぶ必要があるのか、それはクラウドネイティブな開発やマイクロサービスの普及によって、Go言語の需要が急速に高まっているからである。高速な開発と高いパフォーマンスを両立できるGo言語を習得することで、エンジニアとしての市場価値を高めることが可能だ。という建前であるが、
最近の開発者としてGo言語を触れておきたいという雑な感情で学んでみた。

この記事では、Go言語の基本的な文法、変数や型、制御構文などを解説する。Go言語の魅力を感じながら、一緒にシンプルで高速な開発体験を始めよう。

Go言語の歴史と背景

Go言語誕生の背景

Go言語は、2009年にGoogleによって開発されたオープンソースのプログラミング言語である。その誕生の背景には、Google内部で使用されていた**C++**に関する課題が存在した。

  • コンパイル時間の長さ:C++は実行速度が速い一方、コンパイルに非常に時間がかかる。大規模なプロジェクトでは、1台のマシンでビルドに1日以上かかることもあった。
  • 安全性の問題:C++は自由度が高いため、安全にコードを書くことが難しいという課題が存在した。

これらの問題を解決するために、コンパイルが速く、安全にコードが書ける言語としてGo言語が開発された。

特徴的なコンパイラ

初期のGo言語には、「6g/8g」と「gccgo」という2種類のコンパイラが存在した。

  • 6g/8g:Goの全機能をサポートし、コンパイル速度が非常に速い。ただし、生成されるバイナリの最適化が不十分。
  • gccgo:GCC(GNU Compiler Collection)の最適化機能を利用し、効率的なコードを生成可能。しかし、Goの一部機能をサポートしておらず、コンパイル速度も遅い。

ジェネリックの未サポート

当初、Go言語はジェネリックをサポートしていなかった。その代替として、空インターフェースタイプスイッチを用いて柔軟な型の扱いが可能であった。なお、最新のGoではジェネリクスが正式にサポートされている。

組み込みシステムへの適性

Go言語は動的型付けやリフレクションをサポートしているため、バイナリサイズが大きくなりがちであるため、当初は組み込みシステムでの利用に向いていないとされた。しかし、ハードウェアの性能向上により、現在では組み込み分野でも採用が進んでいる。

実際の利用例と展望

2010年時点で、Go言語の公式サイトである golang.org のWebサーバーはGoで記述されていた。開発者の一人であるRob Pike氏は、将来的にすべてのプログラムをGoで書くことを目標としている。

参考

https://xtech.nikkei.com/it/article/Interview/20100526/348493/

Go言語の開発環境構築

Goのインストール方法

まずは、Go言語の公式サイトから最新のバージョンをダウンロードする。Go公式ダウンロードページ

自分のOS(Windows、macOS、Linux)に合わせたインストーラーをダウンロードし、指示に従ってインストールを完了させること。

エディタの設定

開発には、お好みのエディタを使用できるが、以下のエディタがおすすめである。

  • Visual Studio Code (VS Code):軽量で拡張性が高く、Go向けの拡張機能も豊富。
  • GoLand:JetBrains社が提供するGo専用の統合開発環境(IDE)で、強力な機能を備えている。

VS CodeでのGo開発環境設定例:

  1. VS Codeを開く
  2. 拡張機能マーケットプレイスで「Go」を検索
  3. 「Go」拡張機能をインストール
  4. 必要に応じて推奨される追加のツールをインストール

godocのインストールと使い方

Goの公式ドキュメントをローカル環境で閲覧できる godoc ツールをインストールしよう。

ターミナルで以下のコマンドを実行する。

go install golang.org/x/tools/cmd/godoc@latest

インストールが完了したら、godocを起動できる。

godoc -http=:6060

ブラウザで http://localhost:6060 にアクセスすると、Goのドキュメントを閲覧可能である。

godocがないと言われた時

% godoc -http=:6060                     
zsh: command not found: godoc
vi ~/.zshrc

で、以下を登録して保存

export GOPATH=$(go env GOPATH)
export PATH=$PATH:$GOPATH/bin

最後に、以下のコマンドを実行すると使えます

source ~/.zshrc

Hello, World! と基本的な文法

早速、Go言語でプログラムを書いてみよう。

fmtパッケージを使った出力

Go言語で最も基本的なプログラム、「Hello, World!」を表示する。

package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}

実行結果:

Hello, World!

解説:

  • package main:実行可能なプログラムのエントリポイントを示す。
  • import "fmt":標準パッケージのfmtをインポートする。
  • func main():プログラムの開始点となる関数。
  • fmt.Println():標準出力に文字列を表示する。

init関数とmain関数の役割

init関数は、パッケージが初期化される際に自動的に実行される関数である。

package main

import "fmt"

func init() {
    fmt.Println("初期化処理")
}

func main() {
    fmt.Println("メイン処理")
}

実行結果:

初期化処理
メイン処理

ポイント:

  • init関数は複数定義可能。
  • init関数はmain関数よりも先に実行される。

importの使い方と複数パッケージのインポート方法

複数のパッケージをインポートする場合は、以下のように()を使ってまとめる。

package main

import (
    "fmt"
    "time"
)

func main() {
    fmt.Println("現在の時刻:", time.Now())
}

実行結果:

現在の時刻: 2023-10-01 12:00:00 +0000 UTC

os/userパッケージでカレントユーザー情報を取得

現在のユーザー情報を取得する例。

package main

import (
    "fmt"
    "os/user"
)

func main() {
    user, err := user.Current()
    if err != nil {
        fmt.Println(err)
        return
    }
    fmt.Println("現在のユーザー:", user.Username)
}

実行結果:

現在のユーザー: your_username

変数と定数

変数の宣言と初期化

Goでは、以下のように変数を宣言・初期化する。

varキーワードを使う方法

var a int = 10
var b = 20 // 型は自動的に推論される
var s string = "Go言語"

短縮宣言(:=)を使う方法

a := 10
b := 20
s := "Go言語"

例:

package main

import "fmt"

func main() {
    var a int = 10
    b := 20
    s := "Go言語"
    fmt.Println(a, b, s)
}

実行結果:

10 20 Go言語

複数変数の同時宣言

var (
    x int
    y int
    z string
)

または、

x, y, z := 1, 2, "三"

var:= の使い分け

  • var:関数の外でも使える。
  • :=:関数内でのみ使える。

例:

package main

import "fmt"

var i int = 100

func main() {
    i := 200 // 関数内では新しい変数として宣言
    fmt.Println(i) // 200 が表示される
}

定数の宣言

定数はconstキーワードを使って宣言する。

const pi = 3.14
const (
    Sunday = 0
    Monday = 1
)

定数式

定数は計算式を使って定義することも可能。

const area = pi * 2 * 2 // 定数piを使った計算

型と型変換

基本型

  • 整数型:int, int32, int64 など
  • 浮動小数点数型:float32, float64
  • 文字列型:string
  • 論理型:bool

型変換

Goでは、暗黙的な型変換は行われないため、明示的に型変換を行う必要がある。

例:

package main

import (
    "fmt"
    "strconv"
)

func main() {
    var i int = 10
    var f float64 = float64(i)
    fmt.Println(f) // 10.0

    var s string = "20"
    var j int
    var err error
    j, err = strconv.Atoi(s)
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(j) // 20
    }
}

strconv.Atoi の内部動作解説

strconv.Atoi は文字列を整数に変換する。

  • 高速パス:短い文字列は高速に処理される。
  • 低速パス:長い文字列や特定の条件下では詳細な処理が行われる。

配列

配列の宣言と初期化

var arr [3]int // 要素数3の整数型配列
arr[0] = 10
arr[1] = 20
arr[2] = 30

// 簡潔な初期化
arr := [3]int{10, 20, 30}

要素へのアクセス

fmt.Println(arr[0]) // 10
fmt.Println(arr[1]) // 20
fmt.Println(arr[2]) // 30

制御構文

defer

関数の実行が終了する直前に指定した処理を実行する。

例:

package main

import "fmt"

func main() {
    defer fmt.Println("終了処理")
    fmt.Println("メイン処理")
}

実行結果:

メイン処理
終了処理

panicrecover

panicは実行時エラーを引き起こし、recoverはそのパニック状態から復旧する。

例:

package main

import "fmt"

func main() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("エラーを回復しました:", r)
        }
    }()

    panic("予期せぬエラー")
}

実行結果:

エラーを回復しました: 予期せぬエラー

エラーハンドリングの重要性

Goでは、panicを多用せず、エラーは戻り値として処理することが推奨されている。

ポインタ

ポインタの基本概念

ポインタは、変数のメモリアドレスを保持する。

ポインタの宣言と使い方

var n int = 100
var p *int = &n // nのアドレスを取得してポインタpに代入

fmt.Println(*p) // 100(ポインタを介して値を取得)

関数へのポインタ渡し

func increment(n *int) {
    *n = *n + 1
}

func main() {
    var num int = 10
    increment(&num)
    fmt.Println(num) // 11
}

new 関数

new関数は指定した型のゼロ値を持つメモリを割り当て、ポインタを返す。

var p *int = new(int)
*p = 100
fmt.Println(*p) // 100

makenew の違い

make 関数

  • スライス、マップ、チャネルの初期化に使用
  • 具体的な初期化が行われる

例:

s := make([]int, 0)
m := make(map[string]int)
ch := make(chan int)

new 関数

  • 指定した型のゼロ値を持つメモリを割り当て、ポインタを返す
  • 値型のオブジェクトのポインタが必要なときに使用

例:

p := new(int)
fmt.Println(*p) // 0

使い分け

  • make:スライス、マップ、チャネルなどの初期化
  • new:その他の型のポインタを取得したいとき

構造体

Go言語の構造体は、フィールドの集まりでデータをまとめることができる。

構造体の宣言と初期化

type Person struct {
    Name string
    Age  int
}

func main() {
    person := Person{Name: "太郎", Age: 30}
    fmt.Println(person)
}

実行結果:

{太郎 30}

フィールドへのアクセス

fmt.Println(person.Name) // 太郎
fmt.Println(person.Age)  // 30

まとめ

今回は、Go言語の基本的な文法や特徴について雑にまとめてみた。
今後は、part2を書き、開発へ落とし込んでいきたい

Discussion