homebrewでインストール
$ brew update
$ brew install go
環境変数GOPATHを設定する
$ export GOPATH=$HOME/go
Zip圧縮ファイル版が提供されています
- 展開先のディレクトリは
C:\Go
を推奨。 - 上記以外に展開する場合は、該当ディレクトリを環境変数
GOROOT
に指定すること。 - GOPATHはGOROOT/binを指定するとのこと
以下のコマンドの出力結果を確認してみよう
$ go version
$ go env
Vimを使っている場合は、fatih/vim-goプラグインを使うとGo開発に必要なものが一緒にインストールされて便利。
- gocode
- goimports
- godef
- oracle
- golint
- errcheck
- gotags
main.go
package main
import "fmt"
func main() {
fmt.Println("Hello, Fukuoka.go")
}
$ go run main.go
型を指定した宣言が基本だが、初期化も同時に行う場合は型を省略することができる。
// varを使った変数の宣言
var a int
// 初期値を与えることができる
var b int = 1
// 初期値があれば型は省略できる
var c = 2
// := を使った宣言と初期値設定
d := 3
fmt.Println(a, b, c, d)
- 宣言済の変数aに
:=
を使って値を設定できるか確認しよう - 宣言済の変数a, 未宣言の変数eに
:=
を使って値を設定できるか確認しよう - main関数外で
:=
を用いた変数宣言ができるか確認しよう
// const を使った定数の宣言と初期化
const A int = 1
// 型を省略可能
const B = 1
// グルーピング宣言とiota列挙子
const (
X = iota // 0
Y // 1
Z // 2
)
// for
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
fmt.Println(sum)
// 条件文のみのfor
sum = 0
for sum < 100 {
sum += sum
}
fmt.Println(sum)
// 無限ループ
for {
}
// if
var x int // 初期値は0
if x == 0 {
fmt.Println("Zeroed")
}
// 条件式の前に文を書くことができる
if y := myFunc(); y == 0 {
fmt.Println("Zero")
} else if y == 1 {
// 宣言した値はここでも使える
fmt.Println(y)
}
// switch
switch runtime.GOOS {
case "darwin":
fmt.Println("OS X") // breakは不要
default:
fmt.Println("Other")
}
// caseには式を書くことができる
os := runtime.GOOS
switch { // 省略時は switch true と同じ
case os == "darwin":
fmt.Println("OS X")
default:
fmt.Println("Other")
}
- breakは不要。明示的に次のcaseも評価させるときは
fallthrough
を使う
Go言語の関数は多値を返すことができる
func main() {
// 関数の実行
result := add(1, 2)
fmt.Println(result)
// 多値を受け取る関数の実行
a, b := swap("hello", "fukuoka.go")
fmt.Println(a, b)
// errorが発生したか確認する
result, err := div(2, 0)
if err != nil {
fmt.Printf("error: %s", err)
}
}
// 関数の定義
func add(x int, y int) int {
return x + y
}
// 多値を返すことができる
func swap(x, y string) (string, string) {
return y, x
}
// error型を返す多値の例
func div(x, y int) (int, error) {
if y == 0 {
return 0, fmt.Errorf("divide by %d", y)
}
return x / y, nil
}
- 関数の戻り値などで利用していない変数があるとコンパイルできないので注意
- 多値のうち、使わないものがあればブランク識別子
_
に代入すること
Goの関数は一級関数なのでリテラルを使って宣言した無名の関数を変数に格納することができます
func main() {
// First Class Functions
fn := func(s string) {
fmt.Println(s)
}
fn("First Class Functions")
}
Goの関数は クロージャ( closure ) なので、関数の外部から変数を参照することができます
func main() {
// Closures
x := 5
fn := func() {
fmt.Println("x is", x)
}
fn() // x is 5
x++
fn() // x is 6
}
- 外部の変数を利用できるのは便利だが、複数のGoルーチンから外部の変数を更新すると想定しない挙動になるので注意すること
- そういった用途にはチャネルを利用する
Go言語では関数に対する値の受け渡しは値渡しとなり、値はコピーされます。 大きなサイズの値の受け渡しなどを避ける場合などポインタによる参照渡しを行います
// int型の変数iを宣言
i := 1
// int型のアドレスを格納できるポインタの型を宣言
var p *int
// アドレス演算子によって変数からアドレスを得る
p = &i
// 間接参照演算子によってアドレスから変数を得る
fmt.Println(*p) // 1
- pの値を変更することができるか確認しよう
- *pの値を変更することができるか確認しよう
Go言語はクラスではなく「型」を持ちます。 型は値とメソッドを持つことができます
bool
string
int int8 int16 int32 int64
uint uint8 uint16 uint32 uint64 uintptr
byte // uint8 の別名
rune // int32 の別名
// Unicode のコードポイントを表す
float32 float64
complex64 complex128
既存の型をもとに独自の型をつくることができます
// typeキーワードによる型の宣言
type myInt int
// 関数もtypeによる型宣言ができる
type myFunc func(x, y int) int
func main() {
// myInt型の変数を使う
var i myInt = 1
fmt.Println(i + 1)
// 関数リテラルで宣言したmyFunc型の関数を使う
result := callMyFunc(func(x, y int) int {
return x + y
})
fmt.Println(result)
}
func callMyFunc(fn myFunc) int {
return fn(1, 2)
}
- 関数型の宣言は関数の引数や返り値として利用することで定義をまとめたりストラテジーとして扱うと便利
構造体型は複数のフィールドと値を保持します。 メソッドを定義することでクラスのような使い方ができます。
// typeキーワードによる独自の構造体を宣言
type myStruct struct {
i int
s string
}
func main() {
// フィールドの宣言順で初期化
s1 := myStruct{1, "a"}
// フィールド名を指定して初期化
s2 := myStruct{i: 2, s: "b"}
// ポインタの取得
p := &myStruct{1, "a"}
// フィールドへのアクセス
fmt.Println(s1.i, s1.s)
fmt.Println(s2.i, s2.s)
fmt.Printf("p: %s", p)
}
型をレシーバとすることで型に対してメソッドを定義することができます
// レシーバとなる型を指定する
func (s myStruct) print() {
// レシーバ変数を使ってフィールドにアクセスすることができる
fmt.Println(s.i, s.s)
}
// レシーバにポインタを使うことでフィールドの値を変更できる
func (s *myStruct) setInt(value int) {
s.i = value
}
func main() {
s := myStruct{1, "a"}
s.print()
s.setInt(2)
s.print()
}
- ポインタを使わないメソッドでフィールドの値を変更するとどうなるか確認しよう
Go言語のソースはパッケージに属しています。
ソース先頭のpackage
で所属するパッケージが定義されます。
構造体、関数、変数、定数の名前先頭を大文字にすることで外部パッケージからアクセスすることができるようになります。
package sample
// 先頭が大文字なので他のパッケージから利用できる
type MyStruct struct {
Name string
}
// 先頭が大文字なので他のパッケージから利用できる
func MyFunc() {
fmt.Println("MyFunc")
}
- 小文字の場合、外部パッケージからアクセスできないことに注意
- ただしアクセスするためのエクスポートされた関数があれば利用することは可能(シングルトンパターンなど)
外部パッケージを利用するにはインポートが必要です
// importキーワードによるパッケージのインポート
import "sample"
// パッケージ名. に続けて対象を指定する
myStruct := sample.MyStruct{Name: "a"}
sample.MyFunc()
- GitHubなどで公開されているパッケージを利用するには
github.com/user/package
のような指定となります - 公開する自作パッケージは上記のディレクトリ構成でつくっておくと都合がよいです
- 公開されているパッケージを利用するには
go get
コマンドでコードを取得しておきましょう
インターフェースは振る舞いを規定します
// Print()という関数を持つPrinterインターフェース型を宣言
type Printer interface {
Print()
}
// MyStructはPrint()メソッドを持つ
// Printerインターフェースを実装するという宣言は必要ない
type MyStruct struct {
}
func (myStruct MyStruct) Print() {
fmt.Println("MyStruct")
}
func main() {
DoPrint(MyStruct{})
}
// DoPrintメソッドはPrinterインターフェースを引数にとる
func DoPrint(printer Printer) {
printer.Print()
}
- ひとつの役割(関数)しか持たないインターフェースは
XXer
と命名します
Go言語の配列変数は配列全体を表しています(ポインタではありません)
// 配列長と型を指定する
var array1 [2]int
// 宣言と初期化
array2 := [2]int{1, 2}
// 初期化時は配列長を...で代替できる
array3 := [...]int{1, 2}
fmt.Printf("%s, %s, %s", array1, array2, array3)
- 大きなサイズの配列を値渡しする場合は注意
- 配列長をあとから変更することはできません
配列を参照する仕組み。 スライスの長さは動的で、スライス自体は参照型なので配列よりも扱いやすい
// スライス型
var slice []int
fmt.Println(slice)
// 配列からスライス
array1 := [3]int{1, 2, 3}
slice = array1[:] // 配列全体をスライス
fmt.Println(slice)
slice = array1[1:2] // 配列の一部分をスライス
fmt.Println(slice)
// スライスとして宣言する
slice = []int{1, 2, 3}
fmt.Println(slice)
// スライスへの要素の追加
slice = append(slice, 4, 5)
fmt.Println(slice)
// スライスへのスライスの追加
slice2 := []int{6, 7}
slice = append(slice, slice2...)
fmt.Println(slice)
- スライスへの要素の追加は可変長引数でいくつでも渡すことができます
- スライスにスライスを追加するときは
...
が必要なので注意
- もとの配列長を超えて値を追加したらどうなるか考えてみよう (Hint)
キーと値の組み合わせの集合。
// マップ型
var m map[string]string
// makeによるマップの作成
m = make(map[string]string)
// マップとして宣言する
m = map[string]string{
"first": "a",
"second": "b",
}
// 値へのアクセス
fmt.Println(m["first"])
- スライスもマップも参照型だが、マップはもととなる要素(スライスでいう配列)にアクセスすることはできません
- 参照型(スライス、マップ、チャネル)をつくるときはmakeを使います
rangeは要素の集合やチャネルに対する拡張forループを提供します
slice := []string{"a", "b", "c"}
// スライスのforループにrangeを利用する
for i, v := range slice {
fmt.Println(i, v)
}
m := map[string]string{
"first": "a",
"second": "b",
}
// マップのforループにrangeを利用する
for k, v := range m {
fmt.Println(k, v)
}
- チャネルに対するrangeはチャネルがクローズされると終了します
- path/filepathパッケージのWalkを使ってlsコマンドをつくってみよう
- パッケージ、インターフェースを使って実装してみよう
- go build コマンドを使ってバイナリを生成してみよう
処理を並行化するためにはGoroutineを使います。関数をGoroutine化するためには関数の呼出時にgo
キーワードをつけます。
また、Goroutine間の値の共有にはchannel
を使います。
このあたりについては、Go言語でgrep処理をつくりながら使い方を学んでもらえるようなスライドを公開しているので参考にしてみてください。
Go言語は簡単にHTTPサーバを立てることができます
import (
"fmt"
"net/http"
"os"
)
func main() {
http.HandleFunc("/", Hello) // '/'でアクセスされたときに呼ぶハンドラを定義
err := http.ListenAndServe(":8080", nil) // 8080ポートでListen
if err != nil {
os.Exit(1)
}
}
func Hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "<html><body>Hello!!</body></html>")
}
ルーティングやテンプレートエンジンなどを加えたフレームワークについては以下のようなものが続々公開されています。
Go言語の構造体はクラスではないので擬似的な継承しか行えません。
継承ベースの設計よりもインターフェースや移譲を使ったほうが無理なく実装ができるでしょう。
このあたりはGo言語での構造体実装パターンとしてブログに公開しているので参考にしてみてください。