- 投稿日:2020-09-27T22:39:11+09:00
【Go】基本文法② ステートメント
udemy講座の「ステートメント」編を見終わったので、まとめとふりかえり。
今回もコンソール上に出力しながら基本的な動作を学習しました。
前提
・udemyで現役シリコンバレーエンジニアが教えるGo入門 + 応用でビットコインのシストレFintechアプリの開発)を視聴してGoを学習中
※「ブログ等で数行のコードの紹介は可能」とのことなので、講義で扱ったコードを引用しています。・開発環境はVSCode + プラグイン(Goのバージョンは1.15)
・OSはWindows
・普段はJavaを使用しています。if文・switch文
・書き方はjavaと似ている。
・if文で条件文に括弧をつけるとエラーになる。
(癖で括弧を記述しても、VSCodeが自動で括弧を取り除いてくれます。)for文
・for文もjavaと同じ書き方でOK。
・"range"を使用するとより簡単に記述できる。
・"range"はmapでも使用可能。講座で出題された演習問題の自分の回答と解答例を比べると、javaのような記述とrangeを使用した記述の違いが分かりました。どちらも同じ結果が得られますが、rangeを使用する方がすっきり記述できます。rangeはmapも扱えるので、応用も効きそうです。
問題:以下のスライスから一番小さい数を探して出力するコードを書いてください。
l := []int{100, 300, 23, 11, 23, 2, 4, 6, 4}
私の回答l := []int{100, 300, 23, 11, 23, 2, 4, 6, 4} min := l[0] for i := 0; i < len(l); i++ { if min >= l[i] { min = l[i] } } fmt.Println(min)解答例l := []int{100, 300, 23, 11, 23, 2, 4, 6, 4} var min int for i, num := range l { if i == 0 { min = num continue } if min >= num { min = num } } fmt.Println(min)defer
・deferで記述した処理は、関数の処理が終わった後に実行される。
・ファイルのクローズに使用すると便利。
・スタック式で実行される。(はじめに記述した処理が最後に実行される。)例:ファイルの内容をコンソールへ出力するpackage main import ( "fmt" "os" ) func main() { file, _ := os.Open("./lesson.go") // ファイルは必ずクローズする必要がある。最初にdeferで書いておくとクローズし忘れることがない。 defer file.Close() // ファイルを読み込む際は、データとしてバイト配列を用意する必要がある。 data := make([]byte, 100) file.Read(data) // stringにキャスト fmt.Println(string(data)) }ログ
・標準ライブラリ(log)では最低限のログ出力しかできない。
・詳細なログが必要な場合は、サードパーティのライブラリを利用する。
・Fatalln()では、その時点で処理が強制終了されるので、後続処理が行われない。代表的なログ出力関数log.Println("logging!") log.Printf("%T %v", "test", "test") log.Fatalln("error!") // この時点でコートが終了する。 log.Fatalf("%T %v", "test", "test") // 実行されないエラーハンドリング
・try-catchではなく、ifでハンドリングする。
・panic(例外)をプログラム中に記述することは非推奨。受け取ったエラーをif文で処理することが推奨されている。所感
・短い記述でよくある処理(配列操作・ファイルクローズ・ログ出力)ができるので便利。
・関数の挙動や推奨される記述を正確に理解しておかなければならないので、難易度が高い。
・複雑な処理を行うには、標準ライブラリの関数だけでは限界がありそう。
<前回の投稿>
- 投稿日:2020-09-27T22:39:11+09:00
【Go】基本文法まとめ②
udemy講座の「ステートメント」編を見終わったので、まとめとふりかえり。
今回も基本的な動作をコンソール上に出力しながら学習しました。
前提
・udemyで現役シリコンバレーエンジニアが教えるGo入門 + 応用でビットコインのシストレFintechアプリの開発)を視聴してGoを学習中
※「ブログ等で数行のコードの紹介は可能」とのことなので、講義で扱ったコードを引用しています。・開発環境はVSCode + プラグイン(Goのバージョンは1.15)
OSはWindows
・普段はJavaを使用しています。if文・switch文
・書き方はjavaと似ている。
・if文で条件文に括弧をつけるとエラーになる。
(癖で括弧を記述しても、VSCodeが自動で括弧を取り除いてくれます。)for文
・for文もjavaと同じ書き方でOK。
・"range"を使用するとより簡単に記述できる。
・"range"はmapでも使用可能。講座で出題された演習問題の自分の回答と解答例を比べると、javaのような記述とrangeを使用した記述の違いが分かりました。どちらも同じ結果が得られますが、rangeを使用する方がすっきり記述できます。rangeはmapも扱えるので、応用も効きそうです。
問題:以下のスライスから一番小さい数を探して出力するコードを書いてください。
l := []int{100, 300, 23, 11, 23, 2, 4, 6, 4}
私の回答l := []int{100, 300, 23, 11, 23, 2, 4, 6, 4} min := l[0] for i := 0; i < len(l); i++ { if min >= l[i] { min = l[i] } } fmt.Println(min)解答例l := []int{100, 300, 23, 11, 23, 2, 4, 6, 4} var min int for i, num := range l { if i == 0 { min = num continue } if min >= num { min = num } } fmt.Println(min)defer
・deferで記述した処理は、関数の処理が終わった後に実行される。
・ファイルのクローズに使用すると便利。
・スタック式で実行される。(はじめに記述した処理が最後に実行される。)例:ファイルの内容をコンソールへ出力するpackage main import ( "fmt" "os" ) func main() { file, _ := os.Open("./lesson.go") // ファイルは必ずクローズする必要がある。最初にdeferで書いておくとクローズし忘れることがない。 defer file.Close() // ファイルを読み込む際は、データとしてバイト配列を用意する必要がある。 data := make([]byte, 100) file.Read(data) // stringにキャスト fmt.Println(string(data)) }ログ
・標準ライブラリ(log)では最低限のログ出力しかできない。
・詳細なログが必要な場合は、サードパーティのライブラリを利用する。
・Fatalln()では、その時点で処理が強制終了されるので、後続処理が行われない。代表的なログ出力関数log.Println("logging!") log.Printf("%T %v", "test", "test") log.Fatalln("error!") // この時点でコートが終了する。 log.Fatalf("%T %v", "test", "test") // 実行されないエラーハンドリング
・try-catchではなく、ifでハンドリングする。
・panic(例外)をプログラム中に記述することは非推奨。受け取ったエラーをif文で処理することが推奨されている。所感
・短い記述でよくある処理(配列操作・ファイルクローズ・ログ出力)ができるので便利。
・関数の挙動や推奨される記述を正確に理解しておかなければならないので、難易度が高い。
・複雑な処理を行うには、標準ライブラリの関数だけでは限界がありそう。
- 投稿日:2020-09-27T22:17:14+09:00
gRPCで使う.protoファイルの書き方まとめ
- gRPCでは、Protocol Buffersのフォーマットでシリアライズしてデータのやり取りを行う。
.proto
ファイルにてスキーマ定義を行い、ツールを使ってコード生成ができる。そのため、クライアント・サーバーそれぞれでこの.protoファイルを共有できれば、仕様のズレなく開発をすすめることが可能。- この記事では、.protoファイルの文法をざっとまとめてみる
サンプル全体
// バージョン定義 syntax = "proto3"; // パッケージ定義 package sample; // import import "google/protobuf/timestamp.proto"; import "google/protobuf/duration.proto"; // サービスとRPCメソッド定義 service SampleService { rpc Sample (SampleRequest) returns (SampleResponse); // サーバーストリーミングRPC rpc SampleServerStreamMethod (SampleRequest) returns (stream SampleResponse); // クライアントストリーミングRPC rpc SampleClientStreamMethod (stream SampleRequest) returns (SampleResponse) // 双方向ストリーミングRPC rpc SampleBidirectionalMethod (stream SampleRequest) returns (stream SampleResponse) } message SampleRequest { string name = 1; } message SampleResponse { Sample sample = 1; } // メッセージ型 // 右の数字は「タグナンバー」 message Sample { // スカラー型 // 数値、文字列、真偽値、バイト配列がある int32 id = 1; string name = 2; bool isBool = 3; // deprecated 廃止予定かつ非推奨であるフィールドを明示する string duplicated_field = 4 [deprecated = true] // reserved識別子 廃盤にしたタグナンバー reserved 7, 8, 10 to 12; // リスト(配列) // 多次元配列は定義できない repeated SampleList sample_list = 5; // マップ(連想配列) map<string, string> sample_map = 6; // 複数の中からどれかひとつ oneof message { string one = 1; string other = 2; } // Well Known Types google.protobuf.Duration sample_duration = 9; google.protobuf.Timestamp create_time = 13; // 列挙型 enum SampleEnum { UNKNOWN = 0; TEST1 = 1; TEST2 = 2; TEST3 = 3; } }バージョンの定義
syntax = "proto3";パッケージの定義
- 他の.protoファイルで定義したメッセージを使うときなどに、名前の衝突を避けるためにパッケージ名を設定できる
package sample;import
- 他の.protoファイルで定義したメッセージ型を使いたい場合に使う
- ここではGoogleがつくった.protoファイルをimportしている
import "google/protobuf/timestamp.proto"; import "google/protobuf/duration.proto";サービスとRPCメソッド
- APIの定義本体
- SampleServiceというサービスを定義
- SampleRequestを引数に取り、SampleResponseを返すSampleというメソッドを定義している
- ストリーミングにしたい場合は引数、戻り値にstreamをつける
- 単方向ストリーミング
- サーバーストリーミングRPC(戻り値にstreamを設定)
- サーバー側が複数のレスポンスを非同期で返す
- クライアントストリーミングRPC(引数にstreamを設定)
- クライアント側から複数のリクエストを非同期で送り、サーバー側は1つのレスポンスを返す
- 双方向ストリーミング
- 引数、戻り値の両方にstreamをつける
service SampleService { rpc Sample (SampleRequest) returns (SampleResponse); // サーバーストリーミングRPC rpc SampleServerStreamMethod (SampleRequest) returns (stream SampleResponse); // クライアントストリーミングRPC rpc SampleClientStreamMethod (stream SampleRequest) returns (SampleResponse); // 双方向ストリーミングRPC rpc SampleBidirectionalMethod (stream SampleRequest) returns (stream SampleResponse); }スカラー型
- 数値、文字列、真偽値、バイト配列がデータ型としてある
- デフォルト値
データ型 デフォルト値 string 空文字 bytes 空配列 bool false 数値 0 enum 最初に定義された値。必ず0にする メッセージ型 実装依存 repeated 空配列 メッセージ型
- 複数のフィールドを持った型
- タグナンバー
- フィールドの右側の数字。
- 同一メッセージ内で一意にしなければならない
- reserved識別子
- フィールド削除などで廃盤にしたタグナンバーを記載する
- deprecatedオプション
- 廃止予定かつ非推奨のフィールドを明示する
message Sample { // スカラー型 // 数値、文字列、真偽値、バイト配列がある int32 id = 1; string name = 2; bool isBool = 3; // deprecated 廃止予定かつ非推奨であるフィールドを明示する string duplicated_field = 4 [deprecated = true] // reserved識別子 廃盤にしたタグナンバー reserved 7, 8, 10 to 12; // 〜 中略 〜 }リスト(配列)
- 型の前にrepeatedをつけることで配列を定義できる
- スカラー型・メッセージ型ともに使用可能
- 多次元配列は定義できない
// リスト(配列) // 多次元配列は定義できない repeated SampleList sample_list = 5;マップ(連想配列)
- キーには整数値、文字列、真偽値のみ使える
- mapは配列にできない
map<string, string> sample_map = 6;列挙型
- 型の前にenumをつけることで列挙型を定義できる
// 列挙型 enum SampleEnum { UNKNOWN = 0; TEST1 = 1; TEST2 = 2; TEST3 = 3; }oneof
- oneofをつけることで、複数のものから1つという定義ができる
- ここでは
one
かother
を返すメッセージ型になるoneof message { string one = 1; string other = 2; }Well Known Types
- Googleが定義したメッセージ型
- 日時・期間や、戻り値を返さないことを表すものなどがある
google.protobuf.Duration sample_duration = 9; google.protobuf.Timestamp create_time = 13;コードの生成
- 上記で定義した.protoファイルをもとに、クライアント・サーバーの両サイドで各言語用のprotocコマンドを使ってボイラープレートコードを生成する
参考
- 投稿日:2020-09-27T22:12:10+09:00
【Golang】Go言語基礎 Goroutineとは?
本日はgoroutineについて勉強しましたのでここでアウトプットさせて頂きます!
Goroutineとは?
Goroutineとはgoステートメントで関数を指定することで、並行実行されるものです!
まずは普通の関数を書いてみます。
package main import ( "fmt" "time" ) func goroutine(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func hoge(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { goroutine("world") hoge("hello") }実行結果 world world world world world hello hello hello hello helloこれは、普通に上から関数が0.1秒ごとに実行されているだけですね。
これを並行実行するためには、関数名の前にgoを付けるだけで並行実行できます。
(同じなので省略) func main() { go goroutine("world") hoge("hello") }実行結果 hello world world hello world hello hello world world hello並行実行しているので出力が混じった状態になります。
このように goステートメントを指定するだけで並行実行が簡単に実現できます。ここで、timeの部分をコメントアウトしてみるとどうなるでしょうか?
package main import ( "fmt" ) func goroutine(s string) { for i := 0; i < 5; i++ { //time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func hoge(s string) { for i := 0; i < 5; i++ { //time.Sleep(100 * time.Millisecond) fmt.Println(s) } } func main() { go goroutine("world") hoge("hello") }実行結果 hello hello hello hello hellotimeの部分をコメントアウトすると、helloしか出力されなくなりました。
これは、並列処理でgoroutineスレッドが生成されても、先にmain関数の処理が終わってしまったため、
goroutine関数が実行されずに終わってしまったからです。このようにgorutineの処理が終わらなくてもプログラムのコードは終了してしまうということを覚えておきましょう。
ではこれを避けるためにはどうすればよいか?
sync.WaitGroupというのを使います。
sync.WaitGroup
package main import ( "fmt" "sync" ) func goroutine(s string, wg *sync.WaitGroup) { for i := 0; i < 5; i++ { fmt.Println(s) } wg.Done() } func hoge(s string) { for i := 0; i < 5; i++ { fmt.Println(s) } } func main() { var wg sync.WaitGroup wg.Add(1) go goroutine("world", &wg) hoge("hello") wg.Wait() }このように
wg
という変数を宣言してwg.Add(1)
で一個の並列処理があるということを記述し、
goroutine関数へwg
のアドレスを引数で渡してあげます。そして、
wg.Wait()
と書いてあげることで、gorutine関数のwg.Done()
が呼ばれるまで処理が終わるのを待ってくれます。
このように書くことでgoroutine関数が実行されずに処理が終わってしまうことを回避することができます。ちなみに、
wg.Add(1)
でgoroutineの処理が終わるのを待っているので、wg.Done()
をコメントアウトするとエラーになります。
wg.Add
したらwg.Done()
を呼び出して処理が完了したことを示す必要があります。またgoroutine関数は以下のように書くこともできます。
func goroutine(s string, wg *sync.WaitGroup) { defer wg.Done() for i := 0; i < 5; i++ { fmt.Println(s) } }deferステートメントを使うとその関数の処理が終わった後にwg.Done()が実行されるので、このように書くこともできるのです。
最後まで読んでいただきありがとうございます!
間違いや感想などありましたらコメントいただけると嬉しいです!!
- 投稿日:2020-09-27T16:32:47+09:00
Golang + Gin + Dockerのホットリロード、VSCodeデバッグ環境の構築
概要
Go言語のフレームワークGinの開発環境の構築。(ホットリロード、VSCodeデバッグ対応)
今回のコードはGitHubに公開してあります。
https://github.com/yolo-lin/go-demo環境
- MacOS 10.15.6
- go version go1.15.2 darwin/amd64
導入するパッケージ
Gin:Goのフレームワーク
cosmtrek/air:ホットリロード
delve:デバッグ事前準備
- Goのインストール:公式からインストール
- Go Modulesで依存関係を管理するため、環境変数GO111MODULEをonにする
.zshrcexport GO111MODULE=on
- Go Modulesの初期化
/src$ go mod init パッケージ名
ディレクトリ構成
Ginのディレクトリ構成は特に決まってないので、以下は自己流です。
メインのソースコードは全部srcの下に配置する。今後はmodels
やcontrollers
もsrcに追加する予定です。. ├── .vscode │ └── launch.json ├── docker │ ├── go │ │ └── Dockerfile │ └── mysql │ └── Dockerfile ├── src │ ├── go.mod │ ├── go.sum │ ├── .air.toml │ └── main.go │ └── docker-compose.ymlDocker
Go
/docker/go/DockerfileFROM golang:1.15.2 ENV GO111MODULE=on COPY src/ /go/src/app/ WORKDIR /go/src/app # cosmtrek/airのインストール RUN go get -u github.com/cosmtrek/air # delveのインストール RUN go get -u github.com/go-delve/delve/cmd/dlv # airの起動 CMD air -c .air.tomlMySQL
/docker/go/DockerfileFROM mysql:8.0 RUN chown -R mysql /var/lib/mysql && \ chgrp -R mysql /var/lib/mysqldocker-compose
./docker-compose.ymlversion: '3' networks: backend: driver: bridge services: go: container_name: go build: context: . dockerfile: ./docker/go/Dockerfile ports: - 8080:8080 - 2345:2345 links: - mysql tty: true volumes: - ./src:/go/src/app depends_on: - mysql security_opt: - seccomp:unconfined cap_add: - SYS_PTRACE networks: - backend mysql: container_name: mysql build: context: . dockerfile: ./docker/mysql/Dockerfile environment: MYSQL_USER: root MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: demo hostname: mysql ports: - "3306:3306" volumes: - ./docker/mysql/data:/var/lib/mysql security_opt: - seccomp:unconfined networks: - backend phpmyadmin: image: phpmyadmin/phpmyadmin environment: - PMA_ARBITRARY=1 - PMA_HOST=mysql - PMA_USER=root - PMA_PASSWORD=password ports: - "8081:80" depends_on: - mysql networks: - backendcosmtrek/airの設定
公式のair_example.tomlを流用
以下デバッグ用の記述を追記
/src/.air.toml# Debug #full_bin = "APP_ENV=dev APP_USER=air /go/bin/dlv exec ./tmp/main --headless=true --listen=:2345 --api-version=2 --accept-multiclient"/src/.air.toml# Config file for [Air](https://github.com/cosmtrek/air) in TOML format # Working directory # . or absolute path, please note that the directories following must be under root. root = "." tmp_dir = "tmp" [build] # Just plain old shell command. You could use `make` as well. cmd = "go build -o ./tmp/main ." # Binary file yields from `cmd`. bin = "tmp/main" # Customize binary. full_bin = "APP_ENV=dev APP_USER=air ./tmp/main" # Debug #full_bin = "APP_ENV=dev APP_USER=air /go/bin/dlv exec ./tmp/main --headless=true --listen=:2345 --api-version=2 --accept-multiclient" # Watch these filename extensions. include_ext = ["go", "tpl", "tmpl", "html"] # Ignore these filename extensions or directories. exclude_dir = ["assets", "tmp", "vendor", "frontend/node_modules"] # Watch these directories if you specified. include_dir = [] # Exclude files. exclude_file = [] # This log file places in your tmp_dir. log = "air.log" # It's not necessary to trigger build each time file changes if it's too frequent. delay = 1000 # ms # Stop running old binary when build errors occur. stop_on_error = true # Send Interrupt signal before killing process (windows does not support this feature) send_interrupt = false # Delay after sending Interrupt signal kill_delay = 500 # ms [log] # Show log time time = false [color] # Customize each part's color. If no color found, use the raw app log. main = "magenta" watcher = "cyan" build = "yellow" runner = "green" [misc] # Delete tmp directory on exit clean_on_exit = trueこれで、
docker-compose up -d
したら、自動的に設定ファイルを適応し、ホットリロード化される。デバッグ
vscode
Goの拡張機能をインストール、以下デバッグ設定ファイルを追加する。
/.vscode/launch.json{ "version": "0.2.0", "configurations": [ { "name": "Remote", "type": "go", "request": "launch", "mode": "remote", "program": "${workspaceFolder}", "port": 2345, "host": "127.0.0.1", "showLog": true, } ] }デバッグが必要な場合、cosmtrek/airの設定を変更する。
開発用とデバッグ用のfull_bin
を交換し、docker-compose再起動したら、vscodeでデバッグ可能になります。/src/.air.toml# Customize binary. # full_bin = "APP_ENV=dev APP_USER=air ./tmp/main" # Debug full_bin = "APP_ENV=dev APP_USER=air /go/bin/dlv exec ./tmp/main --headless=true --listen=:2345 --api-version=2 --accept-multiclient"ただし、コード更新した場合、cosmtrek/airより自動ビルドされるが、vscode側が切断され、手動で再接続が必要です。
- 投稿日:2020-09-27T16:28:17+09:00
【Go】基本文法① 定義
udemy講座の「定義」編を見終わったので、まとめとふりかえり。
基本的な動作をコンソール上に出力しながら学習しました。
前提
- udemyで現役シリコンバレーエンジニアが教えるGo入門 + 応用でビットコインのシストレFintechアプリの開発)を視聴してGoを学習中
※「ブログ等で数行のコードの紹介は可能」とのことなので、講義で扱ったコードを引用します。
- 開発環境はVSCode + プラグイン(Goのバージョンは1.15)
- OSはWindows
ライブラリ
・goサイトの"document"か、コマンドプロンプトで説明が読める。
・関数外でimportして使用する。変数
・宣言した変数が使用されていないとコンパイルエラーになる。
・まとめて宣言することも可能 ↓var ( a int b float64 c string d bool )・関数の中では、型を指定しなくても宣言できる。(自動で型を判定してくれる。)
・変数の型はfmtライブラリの関数を使用して調べられる。x := 1 // Printfで型を調べられる fmt.Printf("%T", x)・不変な値はconstで宣言する。ふつう関数外に書く。
const ( // constでは型が指定されていない(コンパイラに解釈はされているが、実行はされていない) Username = "test_user" Password = "test_pass" )・他のgoのファイルからも呼び出される場合、変数名は大文字ではじめる。
配列
・要素数を指定して生成した配列は、サイズを変更できない。
・スライスで要素数を指定せずに配列を生成すると、appendで要素を追加できる。
・要素数0の配列の宣言方法は2通り↓// パターン1 初期値での配列をメモリに生成 array1 := make([]int, 0) // パターン2 nil(null的な扱い) var array2 []intMap
・Map型で保存されている。
・キーから値を取り出せる。m := map[string]int{"apple": 100, "banana": 200} // キーから値を取り出せる fmt.Println(m["apple"]) // キーを指定しvalueを更新できる m["banana"] = 300 fmt.Println(m) // 新しい要素の追加も可能 m["new"] = 500 fmt.Println(m)クロージャー
・関数ごと戻り値として設定できる。
・関数内の値は保持される。
・値の動きが分かりにくいので、デバッグツールで動きを追うと良い。func circleArea(pi float64) func(radius float64) float64 { return func(radius float64) float64 { return pi * radius * radius } } func main(){ // はじめに円周率を設定 c1 := circleArea(3.14) c2 := circleArea(3) // 設定した円周率から計算を実行 fmt.Println(c1(2)) fmt.Println(c2(2)) }クロージャーの説明は、こちらのサイトも参考にしました。
Javaとの違い
普段はJavaを使っているので、気になった点がいくつかありました。
・行の最後に「;」が無い。
・クラスが存在しない。
・public/privateを明記しない。
・「変数名 <型>」の順に記述する。(javaと逆)
・未使用の変数が一つでもあると、エラーになる。
・オブジェクト指向ではなく、ライブラリで用意された関数を使用して値を扱う。
(オブジェクトクラスの継承がない。)※Goがオブジェクト指向かどうかについては、「Yes and no」だそうです。(FAQの回答より)
Is Go an object-oriented language?
Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes).
Also, the lack of a type hierarchy makes “objects” in Go feel much more lightweight than in languages such as C++ or Java.所感
・処理が速い。
・関数の記述方法はjavaScriptに近いように感じた。
(javaScriptの知識も浅いので確かではないです。。。JSも勉強します。)
・Mapが扱いやすい。for文で検索しなくても要素を抽出できる。
・udemyでの学習は説明を聞きながら手を動かせるので、効率が良い。
- 投稿日:2020-09-27T16:28:17+09:00
【Go】基本文法まとめ①
udemy講座の「定義」編を見終わったので、まとめとふりかえり。
基本的な動作をコンソール上に出力しながら学習しました。
前提
- udemyで現役シリコンバレーエンジニアが教えるGo入門 + 応用でビットコインのシストレFintechアプリの開発)を視聴してGoを学習中
※「ブログ等で数行のコードの紹介は可能」とのことなので、講義で扱ったコードを引用します。
- 開発環境はVSCode + プラグイン(Goのバージョンは1.15)
- OSはWindows
ライブラリ
・goサイトの"document"か、コマンドプロンプトで説明が読める。
・関数外でimportして使用する。変数
・宣言した変数が使用されていないとコンパイルエラーになる。
・まとめて宣言することも可能 ↓var ( a int b float64 c string d bool )・関数の中では、型を指定しなくても宣言できる。(自動で型を判定してくれる。)
・変数の型はfmtライブラリの関数を使用して調べられる。x := 1 // Printfで型を調べられる fmt.Printf("%T", x)・不変な値はconstで宣言する。ふつう関数外に書く。
const ( // constでは型が指定されていない(コンパイラに解釈はされているが、実行はされていない) Username = "test_user" Password = "test_pass" )・他のgoのファイルからも呼び出される場合、変数名は大文字ではじめる。
配列
・要素数を指定して生成した配列は、サイズを変更できない。
・スライスで要素数を指定せずに配列を生成すると、appendで要素を追加できる。
・要素数0の配列の宣言方法は2通り↓// パターン1 初期値での配列をメモリに生成 array1 := make([]int, 0) // パターン2 nil(null的な扱い) var array2 []intMap
・Map型で保存されている。
・キーから値を取り出せる。m := map[string]int{"apple": 100, "banana": 200} // キーから値を取り出せる fmt.Println(m["apple"]) // キーを指定しvalueを更新できる m["banana"] = 300 fmt.Println(m) // 新しい要素の追加も可能 m["new"] = 500 fmt.Println(m)クロージャー
・関数ごと戻り値として設定できる。
・関数内の値は保持される。
・値の動きが分かりにくいので、デバッグツールで動きを追うと良い。func circleArea(pi float64) func(radius float64) float64 { return func(radius float64) float64 { return pi * radius * radius } } func main(){ // はじめに円周率を設定 c1 := circleArea(3.14) c2 := circleArea(3) // 設定した円周率から計算を実行 fmt.Println(c1(2)) fmt.Println(c2(2)) }クロージャーの説明は、こちらのサイトも参考にしました。
Javaとの違い
普段はJavaを使っているので、気になった点がいくつかありました。
・行の最後に「;」が無い。
・public/privateを明記しない。
・「変数名 <型>」の順に記述する。(javaと逆)
・未使用の変数が一つでもあると、エラーになる。
・オブジェクト指向ではなく、ライブラリで用意された関数を使用して値を扱う。
(オブジェクトクラスの継承がない。)※Goがオブジェクト指向かどうかについては、「Yes and no」だそうです。(FAQの回答より)
Is Go an object-oriented language?
Yes and no. Although Go has types and methods and allows an object-oriented style of programming, there is no type hierarchy. The concept of “interface” in Go provides a different approach that we believe is easy to use and in some ways more general. There are also ways to embed types in other types to provide something analogous—but not identical—to subclassing. Moreover, methods in Go are more general than in C++ or Java: they can be defined for any sort of data, even built-in types such as plain, “unboxed” integers. They are not restricted to structs (classes).
Also, the lack of a type hierarchy makes “objects” in Go feel much more lightweight than in languages such as C++ or Java.所感
・処理が速い。
・関数の記述方法はjavaScriptに近いように感じた。
(javaScriptの知識も浅いので確かではないです。。。JSも勉強します。)
・Mapが扱いやすい。for文で検索しなくても要素を抽出できる。
・udemyでの学習は説明を聞きながら手を動かせるので、効率が良い。
- 投稿日:2020-09-27T15:04:18+09:00
TinyGoの開発環境をVSCode向けにセットアップする
TinyGoのVSCode向け開発環境セットアップがちょっと面倒だったので書き残し。
公式の記事はここ。
TinyGoのインストール・セットアップは他記事をご参照ください。
筆者の環境 : MacBook Air (Retina, 13-inch, 2020) macOS Catalina 10.15.52020/09/27 追記1
.vscode/settings.json
は下記TinyGo拡張機能のインストール・セットアップを行えば自動的に生成されるとのご指摘をいただきました。
なので以下settings.jsonの作成
パートはスキップしてしまって構いません。
TinyGoの設定確認
TinyGoでは本家Goとは別パスにあるGOROOT(?)を使用するようです。1
@sago35 さんよりご指摘頂き、TinyGoはmachine
パッケージなどのTinyGo専用パッケージはTinyGoのROOTより解決し、それ以外のimportパスは通常のGo言語の物を使っている(GOPATHも本家Goと同じ)とのことでした。sago35さんありがとうございます!
tinygo info
でTinyGo用のGOROOTのパスを確認して下さい。$ tinygo info microbit // 出力結果... // LLVM triple: armv6m-none-eabi // GOOS: linux // GOARCH: arm // build tags: cortexm baremetal linux arm nrf51822 nrf51 nrf microbit tinygo gc.conservative scheduler.tasks // garbage collector: conservative // scheduler: tasks // cached GOROOT: /home/user/.cache/tinygo/goroot-go1.14-f930d5b5f36579e8cbd1c139012b3d702281417fb6bdf67303c4697195b9ef1f-syscall
cached GOROOT:...
で始まる行のパスがTinyGo用GOROOTです。
build tags:...
で始まる行がビルド時に使用するオプションです。
settings.jsonの作成
VSCodeの設定を直接いじっても良いのですが、通常のGoを用いた開発に影響を出したくないため、プロジェクトフォルダに.vscode
フォルダを作成しsettings.json
ファイルを作成します。
その際GOROOT
はそのまま貼り付けて問題ないのですが、GOFLAGSは-tags=
を先頭につけ、スペースに代わりにカンマ(,
) を使って区切ります。
例えば筆者の環境ではbuild tags: darwin amd64 tinygo gc.extalloc scheduler.coroutines
という結果だったため、
-tags=darwin,amd64,tinygo,gc.extalloc,scheduler.coroutines
となります。settings.json{ "go.toolsEnvVars": { "GOROOT": "さっき取得したGOROOT...", "GOFLAGS": "-tags=avr,baremetal,linux,arm,atmega328p,atmega,avr5,arduino,tinygo,gc.conservative,scheduler.none" } }TinyGo 拡張機能のインストール
ExtensionsよりTinyGo をインストールします。
インストール後、コマンドパレットより
TinyGo target
を選択し、使っているマイコンを選択します。
- 投稿日:2020-09-27T10:57:47+09:00
GO/Gin gin-swagger使用してAPI仕様書を生成します
前書
この記事は
gin-swagger
ライブラリ使用してGinプロダクトにAPI仕様書生成するための知見をまとめるものになります。
gin-swaggerリポジトリリンク
テスト用のプロダクトを作成
フォルダを作成
// 任意のディレクトリで $ mkdir use_swagger && cd use_swaggergo modを初期化
$ go mod init use_swaggerライブラリインストール
$ go get -u github.com/gin-gonic/gin $ go get -u github.com/swaggo/swag/cmd/swag $ go get -u github.com/swaggo/gin-swagger $ go get -u github.com/swaggo/filesインストール完了後のバージョン確認。
$ swag -v swag version v1.6.7現在のディレクトリ構成
|-- use_swagger |-- |-- go.modプロダクトにAPI仕様書追加
use_swagger
ディレクトリ配下にmain.go
ファイル作成$ touch main.go追加前
main.gopackage main import ( "github.com/gin-gonic/gin" ) func main() { r := gin.New() r.GET("/test", test) r.Run() } func test(c *gin.Context){ c.JSON(http.StatusOK, gin.H{ "msg": "ok"}) }サーバー立ち上げ後
http://localhost:8080/test
にアクセスすると、簡単なJsonデータが戻ってきます。
追加後
main.gopackage main import ( "github.com/gin-gonic/gin" swaggerFiles "github.com/swaggo/files" "github.com/swaggo/gin-swagger" "net/http" _ "use_swagger/docs" ) // @title APIドキュメントのタイトル // @version バージョン(1.0) // @description 仕様書に関する内容説明 // @termsOfService 仕様書使用する際の注意事項 // @contact.name APIサポーター // @contact.url http://www.swagger.io/support // @contact.email support@swagger.io // @license.name ライセンス(必須) // @license.url http://www.apache.org/licenses/LICENSE-2.0.html // @host localhost:8080 // @BasePath / func main() { r := gin.New() url := ginSwagger.URL("http://localhost:8080/swagger/doc.json") r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url)) r.GET("/test", test) r.Run() } // @description テスト用APIの詳細 // @version 1.0 // @accept application/x-json-stream // @param none query string false "必須ではありません。" // @Success 200 {object} gin.H {"code":200,"msg":"ok"} // @router /test/ [get] func test(c *gin.Context){ c.JSON(http.StatusOK, gin.H{ "msg": "ok"}) }仕様書を初期化します。
$ swag init初期化完了後、ディレクトリ構成は以下のようになりました。
|-- use_swagger |-- |-- docs |-- |-- |-- docs.go |-- |-- |-- swagger.json |-- |-- |-- swagger.yaml |-- |-- main.go |-- |-- go.modサーバーを立ち上げ、
http://localhost:8080/swagger/index.html
へアクセスすれば、仕様書が表示されます。
実際使ってみます、適当にパラメータを渡して、実行します。
期待通りのレスポンスが返ってきました。
よく使用されるオプション
API オプション
オプション 説明 description 操作動作の詳細な説明。 id 操作を識別するために使用される一意の文字列。すべてのAPI操作の中で一意である必要があります。(使用されてる所あんまり見ない) tags コンマで区切られた各API操作へのタグのリスト、APIの関連性を示します。 summary 操作の実行内容の簡単な要約。 accept APIが使用できるMIMEヘッダタイプのリスト。値は、MIMEヘッダタイプで説明されているとおりでなければなりません。 produce APIが生成できるMIMEタイプのリスト。値は、MIMEヘッダタイプで説明されているとおりでなければなりません。 param スペースで区切られたパラメーター。パラメータ名、パラメータタイプ、データタイプ、必須ですか?、コメント属性(オプション) security 各API操作のセキュリティ。 success スペースで区切られた成功レスポンス。レスポンスコード、{param type}、データ型、コメント failure スペースで区切られた障害レスポンス。レスポンスコード、{param type}、データ型、コメント router パス、[httpMethod] 使用凡例
// @Summary Auth admin // @Description get admin info // @Tags accounts,admin // @Accept application/x-json-stream // @Produce application/x-json-stream // @Success 200 {object} model.Admin // @Failure 400 {object} httputil.HTTPError // @Failure 401 {object} httputil.HTTPError // @Failure 404 {object} httputil.HTTPError // @Failure 500 {object} httputil.HTTPError // @Security ApiKeyAuth // @Router /admin/auth [post] func (c *Controller) Auth(ctx *gin.Context) { authHeader := ctx.GetHeader("Authorization") if len(authHeader) == 0 { httputil.NewError(ctx, http.StatusBadRequest, errors.New("please set Header Authorization")) return } if authHeader != "admin" { httputil.NewError(ctx, http.StatusUnauthorized, fmt.Errorf("this user isn't authorized to operation key=%s expected=admin", authHeader)) return } admin := model.Admin{ ID: 1, Name: "admin", } ctx.JSON(http.StatusOK, admin) }// ShowBottle godoc // @Summary Show a bottle // @Description get string by ID // @ID get-string-by-int // @Tags bottles // @Accept json // @Produce json // @Param id path int true "Bottle ID" // @Success 200 {object} model.Bottle // @Failure 400 {object} httputil.HTTPError // @Failure 404 {object} httputil.HTTPError // @Failure 500 {object} httputil.HTTPError // @Router /bottles/{id} [get] func (c *Controller) ShowBottle(ctx *gin.Context) { id := ctx.Param("id") bid, err := strconv.Atoi(id) if err != nil { httputil.NewError(ctx, http.StatusBadRequest, err) return } bottle, err := model.BottleOne(bid) if err != nil { httputil.NewError(ctx, http.StatusNotFound, err) return } ctx.JSON(http.StatusOK, bottle) }その他API情報は公式ドキュメントとこちらのリポジトリを参考にしてください。
使用感想
gin-swaggerライブラリは非常に便利とは言えない、コメントから生成されるAPI仕様書であるために、
記述する際には間違えがないかよく確認したほうがいいと思います。