Goを勉強する
Goは構造体なので、継承は存在しない代わりに、構造体の埋め込みを行う事ができる。
また、メソッドの定義も、レシーバを書く事で構造体に紐づける関数を構造体の外で定義する必要があるようだ。
type Food struct {
Name string
Type string
}
// ファンクション名の前にレシーバを書く
func (a Food) name() string {
return a.Name
}
type Fruit struct {
Food
}
func main() {
f := Food{"beef", "meet"}
fmt.Println("This is ", f.name())
}
配列の書き方
// var 変数名[長さ] 型
var arr[2] string
// var 変数名[長さ] 型 = [大きさ]型 {初期値1, 初期値2}
var arr[2] string = [2]string {"hello", "world"}
// 変数名 := [...] 型{初期値1, 初期値2}
var arr[2] string = [2] string{"hello", "world"}
参考記事:
インターフェースについて
インターフェースの定義の仕方
type 型名 interface {
メソッド名1(引数の型, ...) (返り値の型, ...)
...
}
空のインターフェースが存在している。
全ての型と互換性を持っている。
型アサーション
value := <変数>.(<型>)
変数 にインターフェースの変数が入る。
<型>の方にstringやintegerなどが入る。
また、二つ目の返り値も入る。
value, bool := <変数>.(<型>)
ここでboolには、型チェックの結果が返ってくる。
ここで、型チェックがfalseだとしても、falseが返ると言うことでエラーにはならない。
boolがないと型チェックでfalseの場合、エラーとして吐き出される。
型 switch
型 switchというものがある。
func do(i interface{}) {
switch variable := i.(type) {
case int:
fmt.Println(variable)
case string:
fmt.Println(variable)
default:
fmt.Println("Default")
}
}
func main() {
do(23) //=> 23
do("hello") //=> hello
do(true) //=> Default
}
並行処理
GoRoutineとchannelという概念がある。
並行処理で使われる
GoRoutineだけだと、処理の完了を待たずに親ファンクションが完了してしまう。
そこで、channelというのを開き、並行処理(GoRoutine)が全て完了するまで待つことができる。
ch1 := make(chan bool)
…
// この間に並行処理を複数書く
…
<-ch1
GOROOT:
実行されるGoがあるpathを入れる。
// brew経由で1.17.6を入れた場合
export GOROOT=/usr/local/Cellar/go/1.17.6/libexec/
GOPATH:
workspacesのpathを入れる。
// GolandなどのIDEを使ってる場合
export GOPATH=$HOME/GolandProjects/
// ghqなどでcloneしてきてる場合
export GOPATH=$HOME/ghq/{github account}/...
Goで.envの値を取得する
github.com/joho/godotenv
を利用する。
envファイルを読み込む
err := godotenv.Load(".env")
if err != nil {
// err hadnle
}
envから値を読み出す
os.Getenv("VALUE")
cobraを使う
cobra-cli コマンドをインストール
go install github.com/spf13/cobra-cli@latest
cobraをプロジェクトにダウンロード
go get -u github.com/spf13/cobra@latest
プロジェクトでcliを使ってプロジェクトでcobraをinitする
cobra init --license MIT --viper=false
初期動作確認
go run main.go
以下が表示される
A longer description that spans multiple lines and likely contains
examples and usage of using your application. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.
サブコマンドを追加
cobra-cli add version
以下のファイルが作られる(コメントは削除してます)
package cmd
import (
"fmt"
"github.com/spf13/cobra"
)
var versionCmd = &cobra.Command{
Use: "version",
Short: "A brief description of your command",
Long: `A longer description that spans multiple lines and likely contains examples
and usage of using your command. For example:
Cobra is a CLI library for Go that empowers applications.
This application is a tool to generate the needed files
to quickly create a Cobra application.`,
Run: func(cmd *cobra.Command, args []string) {
fmt.Println("version called")
},
}
func init() {
rootCmd.AddCommand(versionCmd)
}
サブコマンドを呼び出す。
go run main.go version
Runの中が実行される
version called
参考
Qiita APIを使う
公式
-
Go で Qiita API を叩いてみる
https://qiita.com/snaka/items/676728e6bf9968d81581 -
Qiita API v2 の概要(非公式)
https://qiita.com/tag1216/items/b0b90e30c7e581aa2b00 -
Qiita API を使って、記事データを取得する方法
https://qiita.com/wakudar/items/8c594c8cc7bda9b93b4e
ファイル操作
YYYY-MM-DD-hh-mm-ss ファイル名で作成する
// data/ 配下に作成
fp, err := os.Create("data/" + time.Format("2000-01-01-15-04-05") + ".json")
参考
go mod edit -replace github.com/XXXX/YYYY=..
とすると、ディレクトリを分けたgo.modの中でも、親のgo.modのパッケージを参照するようになる。
Goでimmutableに配列操作を扱う場合
array := []string{"dummy", "dummy2"}
newArray := append(array[:], "dummy3")
このようにすると、元の配列(array)を壊すことなく、新しい配列を作成することができる。
参考:
OpenFileを行う時の第二引数の意味
os.O_RDONLY: 読み取り専用で開く
os.O_WRONLY: 書き込み専用で開く
os.O_RDWR: 読み取りと書き込みの両方が可能なモードで開く
os.O_APPEND: 書き込み時にファイルの終端に追加する
os.O_CREATE: ファイルが存在しない場合に新規作成する
os.O_TRUNC: ファイルが既に存在する場合はサイズを0に切り詰める(内容を全て削除する)
OpenFileを行う時の第三引数の意味
0644: オーナーは読み書きが可能、他のユーザーは読み取りのみ可能
0755: オーナーは読み書き・実行が可能、他のユーザーは読み取りと実行のみ可能
0666: オーナーと他のユーザー両方が読み書きが可能
init関数
Goの特殊な関数
そのパッケージに含まれるグローバル変数が初期化されたタイミングで自動的に呼ばれる。
constでenumを表現する
stringerを使う
go install golang.org/x/tools/cmd/stringer@latest
stringer -type Fruit fruit.go
go generateを使う
go:generate stringer -type Fruit fruit.go
を書いておくと
go generate
を実行した時に自動で実行される
Goroutineについて
コードは以下に記載
Table Driven Test
コードは以下に記載
前後処理
TestMain という関数を用意する
func TestMain(m *testing.M) {
log.Println("before")
ret := m.Run()
log.Println("after")
os.Exit(ret)
}
Short
以下を埋め込むことで
go test -short
を実行したときにスキップすることができる。
if testing.Short() {
t.SkipNow()
}
Parallel
for _, test := range tests {
test := test // ここをしないとタイミングによって、最後の項目のみ参照されてしまう。
t.Run(test.name, func(t *testing.T) {
t.Parallel()
got := Add(test.lhs, test.rhs)
if got != test.want {
t.Errorf("Add(%d, %d) = %d, want %d", test.lhs, test.rhs, got, test.want)
}
})
}
差分
google/go-cmp を使うと差分を取るのに楽
{
+ "value": 20
- "value": 21
}
テンポラリーディレクトリ
テスト中に一時ファイルを作成したい時がある。
そういった時testing には、一時的にファイルを作る機能が備わっている。
例:
func TestCreateProfile(t *testing.T) {
dir := t.TempDir()
filename := filepath.Join(dir, "profile.json")
got, err := CreateProfile(filename)
if err != nil {
t.Fatalf("CreateProfile(%s) = %v", filename, err)
}
want := true
if got != want {
t.Errorf("CreateProfile(%s) = %v, want %v", filename, got, want)
}
}
環境変数
t.SetEnv("DATABASE_URL")
などのように環境変数を設定することができる。
Fuzzingテスト
GoにはFuzzingtテストのためのツールが用意されている。
予想ができていないテスト項目をテストを行うまで気づけないことに対して、Fuzzingテストを行うことで未然にどういった挙動を行うか知ることができる。
例:
func FuzzDoSomething(f *testing.F) {
f.Add("test&&&")
f.Fuzz(func(t *testing.T, input string) {
DoSomething(input)
})
}
上記がある状態で以下のコマンドを実行
go test -fuzz FuzzDoSomething -fuzztime 10s