2020/02/24

[golang]パッケージ名? ディレクトリ名?

以前、こんな記事を書いた。

hiro99ma blog: [golang]go runの引数はなんなのか
https://hiro99ma.blogspot.com/2020/02/golanggo-run.html

とりあえずmainパッケージを含んでいるディレクトリを指定しておけばいいんじゃないか、くらいで終わった。

 

そういえばimportなんかは基本的にsrc以下からのパスで書いていたはずだ。
import "github.com/hirokuma/gogo/cmd"、とか。
mainパッケージを含んでいるディレクトリを書くということであれば、cmdディレクトリの中がmainパッケージになっていて、どれかのgoファイルの中にmain()があるんだろう。

いつも「mainパッケージ」と呼んでいるが、よく考えれば正式な名前は「github.com/hirokuma/gogo/cmd/mainパッケージ」なのだろうか?

 

runの引数は実行できれば良かったのだが、importとなるとまた話が変わってくることに気付いたのだった。


ここを見よう。

How to Write Go Code - The Go Programming Language
https://golang.org/doc/code.html

読む前の注意点。

This document assumes that you are using Go 1.13 or later and the GO111MODULE environment variable is not set

1.13より古い場合はアーカイブ参照だ。
ページの最終更新日が書いてあると助かるのだが、載ってなさそうだ。
google検索からすると、2020年1月13日みたいだ。

 

翻訳するなら自動でもよさそうなので、気がついたところだけ抜粋しよう。

  • パッケージ、リポジトリ、モジュール
    • たぶん、ファイル単体を別とすれば、パッケージが最小単位
    • リポジトリは1つ以上のモジュールを含む
      • 通常、リポジトリのルートにある1つのモジュールが含まれる
      • go.modとディレクトリのことが書いてあるが、なんか分かりづらいぞ。。
    • モジュールは関連するパッケージの集合
  • A package is a collection of source files in the same directory that are compiled together.
    • パッケージの単位は同一ディレクトリなのね。
  • Each module's path not only serves as an import path prefix for its packages, but also indicates where the go command should look to download it.
    • import pathってことは、importに書いているあれはパスの方なのね。
  • An import path is a string used to import a package. A package's import path is its module path joined with its subdirectory within the module.
    • import pathは、パスといっても純粋なディレクトリじゃなくて、ディレクトリ構造+パッケージ名、なのか

 

よし、なんとなくわかった!
わかったんじゃないだろうか。。。

いろいろコードの書き方に抜け道(コンパイルエラーにならず上記の書き方にしない方法)はあるのだろうが、上記の書き方をしておけば悪くないということだ。

C言語の時は使用期間が長いこともあり、自分でなんとなくルールを決めていった。
Java言語ではディレクトリ構成も含めてきっちり決められていたので、その通りにやりつつも自由度がないなあ、と思った。
その中間くらいなのかな。

[vscode]vscodeとWSLとgolang

仕事でもvscodeを使っているが、それはLinux上で動かしている。
家で使う場合は、VirtualBox上にLinuxを立ち上げ、その環境で動かしている。

最近、何回かgolangの記事を書いたが、それらはVirtualBox + Linuxでやっていて、vscodeを使っていた。
が、golangであればWindowsでも使えるので、そっちの方が軽いだろう。

それに、Linux環境も併用したいなら、WSLを使えばよい。
というわけで、vscodeはWindowsで、それ以外の環境はWSLで作ろうとした。

 

環境はできたのだが、Linuxで動かしていたときはgoのextensionをインストールすると、goファイルを初めて開いたときにツール類のインストールが自動で行われていたものが、Windows + WSLでは行われなかったのだ。
ダイアログで"go env"ができない、みたいなことを言われるのだ。

これは・・・Windows側にgoのバイナリが入っていないからか?

 

と思ったら、単にWSL側の環境変数にgoのぱすをとおしていなかっただけだった。。。
apt installしたから自動で追加されているかと思ったのだが、add repositoryした方だったからか入っていなかった。
PATHに追加すれば、インストールもできました。

よくわからないが、gopkgsのインストールは失敗したので手動でインストールした。
OUTPUTタブに失敗したコマンドが出てくるので、助かるね。

 

WSL側で"explorer.exe ."とすると\\wslの状態で立ち上がる。
同様に、"code.exe ."で立ち上がってくれるかと思ったが、こちらは最後に起動したremote状態がWSLであれば、WSLで起動しているようだ。
vscodeを複数開くと、最初のウィンドウサイズは保存してくれているサイズになるが、2枚目以降は強制的に画面の4分の1くらいになるようなので、最初からWSL状態で立ち上がってくれるのは助かる。
コマンドラインから選択できるようであればショートカットを作っておくのだが、まあよかろう。

[vscode]Open EditorsのShow/Hideがコンテキストメニューになった

Visual Studio Codeのナビゲーション部分(と呼ぶのか?)に、ファイル一覧を表示する"Explorer"がある。
その内部に表示される項目がいくつかあるが、"Open Editors"はsettings.jsonで非表示にできるようになっていたと思う。
"explorer.openEditors.visible"だったと思う。
非表示にしたい理由は、編集するタブが増えるとOpen Editorsの項目が増えて、その下に表示されるディレクトリ表示の位置が下がってしまうからだ。
たたむこともできるのだけど、たたむくらいだったら非表示でいいか、と。


今までその設定をしていたのだが、久々にWindowsのVisual Studio Code v1.42.1を起動するとOpen Editorsが表示されていた。
どうしたらいいんじゃんろうかと触っていたら、コンテキストメニューで設定できるようになっていた。
image
以前からできていたのかもしれないが、気付かなかった。

2020/02/23

[golang]mutexもある

いかんいかん、golang気を抜くと学習を忘れてしまう。
そして、気を抜くとgolang自体忘れかねない。。。

 

goroutineをちょっとだけやったが、そのときはデータの受け渡しにchanを使った。
しかし、スライスを渡せる以上、メモリの共有もできるということだ。
ならばセマフォなりmutexなりの機構があるはずだ。


mutexは、ある。

Go でロックする - Qiita
https://qiita.com/mrasu/items/7531a5a28e9fda77aa5e#%E7%B5%82%E3%82%8F%E3%82%8A%E3%81%AB

 

まあ、mutexはありがちだからよいとして、WaitGroupというものがあるそうだ。

WaitGroupは「Addした回数Doneされるまで待ち続ける」という命令を書けるようにした仕組みです。

セマフォっぽい使い方になるのかな?

waitgroup.goにはAdd() / Done() / Wait()があって、関連するオブジェクトをAddするとかいうものではなく、単にカウントだけの管理をしているようだ。
ちなみにDone()は、Add(-1)になってる。

 

かといって自分で好きな負の値を書いていくのは良くない気がする。
race.ReleaseMerge()は1回だけだし。
いや、そもそもrace.Enableとかやってるけど、raceってWaitGroupオブジェクトごとに持ってるんじゃなくて、importしてるからグローバルな値よね?

・・・race.Enableはconst値じゃった。

 

まだgolangのソースコードに慣れていないのだが、データの持ち方としては、こんな感じだろうか。

  • 外側にある大文字の変数名は、公開されたグローバル変数
  • 外側にある小文字の変数名は、公開されてないグローバル変数(Cでいうところのstatic変数)
  • 関数名の前に括弧して変数定義がある場合は、クラス変数っぽいやつ
  • 関数の引数
  • 関数内で宣言したスタックとかmakeした変数

 

goroutineで共有するなら、グローバル変数やmakeした変数がよかろう。
関数の引数は、元がどうやって宣言されているかによるから、なんともいえんな。


というわけで、簡単にグローバル変数を試すことにした。
mutexの話をしているはずなのに、グローバル変数を試すことになるとは。。

 

src/cmd_ex5/main.go

01: package main
02: 
03: import "ex5"
04: 
05: var MainVal int
06: 
07: func main() {
08: 	MainVal = 20
09: 	ex5.Ex5Val = 30
10: 	ex5.FuncEx5b()
11: 	MainVal = 10
12: 	ex5.Ex5Val = 55
13: 	ex5.FuncEx5b()
14: }
  

 

src/ex5/ex5b.go

01: package ex5
02: 
03: import (
04: 	"fmt"
05: )
06: 
07: var Ex5Val int
08: 
09: func FuncEx5b() {
10: 	fmt.Printf("FuncEx5b: %d\n", Ex5Val)
11: 	//fmt.Printf("MainVal: %d\n", main.MainVal) //compile error
12: }
  

 

$ go run cmd_ex5
FuncEx5b: 30
FuncEx5b: 55

 

main関数は、mainパッケージに置く。
mainパッケージに置いたグローバル変数は、mainパッケージの担当だろう。
しかし、ex5パッケージからはmain.MainValにアクセスできなかった。
逆はできるので、言語仕様だろうか。

まあ、パッケージにpublicなグローバル変数を用意するよりは、getter/setterなんかでアクセスさせた方が安全な気はするが、言語的にできないのとは違う。

ディレクトリ名をmainにしなかったせいか?
しかし、実行ファイルをいくつか作りたい場合はcmd以下にディレクトリを作ろう、というのが推奨だったと思う。
cmd/ex5/mainなんかにしてみたが、ダメだ。
main関数があるとダメなのかと思ったが、そうでもない。

 

うーん、わからん。。

2020/02/16

[golang]配列はデータまるまる値渡し

前回の続き。

前回はスライスだけだったので、今回は配列をやろう。


01: package main
02: 
03: import "fmt"
04: 
05: func gogo(bababa [5]int) {
06: 	for i := range bababa {
07: 		 bababa[i] = 10 + i
08: 	}
09: }
10: 
11: func lolo(bababa [5]int) {
12: 	i := 3
13: 	for i, bababa[i] = range bababa {
14: 		//break
15: 	}
16: }
17: 
18: func main() {
19: 	var momomo [5]int
20: 
21: 	gogo(momomo)
22: 	lolo(momomo)
23: 	for i, val := range momomo {
24: 		fmt.Printf("[%d] = %d\n", i, val)
25: 	}
26: }
  

$ go run .
[0] = 0
[1] = 0
[2] = 0
[3] = 0
[4] = 0

ほう。

ちなみに、前回のスライスで書いたバージョンはこうだ。

01: package main
02: 
03: import "fmt"
04: 
05: func gogo(bababa []int) {
06: 	for i := range bababa {
07: 		 bababa[i] = 10 + i
08: 	}
09: }
10: 
11: func lolo(bababa []int) {
12: 	i := 3
13: 	for i, bababa[i] = range bababa {
14: 		//break
15: 	}
16: }
17: 
18: func main() {
19: 	var momomo []int
20: 
21: 	momomo = make([]int, 5)
22: 	gogo(momomo)
23: 	lolo(momomo)
24: 	for i, val := range momomo {
25: 		fmt.Printf("[%d] = %d\n", i, val)
26: 	}
27: }
  

$ go run ex3
[0] = 11
[1] = 12
[2] = 10
[3] = 14
[4] = 14

前回は要素数を空欄にしていたので、指定しただけなのだが、それで大きく結果が変わった。

gogo()を呼んでいる内容が反映されていないので、コピー渡しされているのだろうか?


ちょうどよい記事があった。

Golang 関数引数のスライスはポインタで渡すべき? - Qiita
https://qiita.com/ta_ta_ta_miya/items/593ed887f58b251c4fe2

 

golanの関数引数は値渡しらしい。
これはC言語もそうなのだが、配列についてはgolangの方がより値渡しになっているな。

C言語の配列は、配列の変数名だけだとアドレスを返す。
配列の中身は、そのアドレスから始まるメモリに書かれている。
ポインタ変数の場合は、そのアドレスにはポインタが指すアドレスが書かれている。

 

golangでも配列はそのアドレスからデータが書かれているのだろう。だから、配列を「値渡し」すると、そのアドレスに書いてあるデータも含めてコピーされる。
スライスの場合はC言語のポインタ変数みたいなもので、アドレスに書かれているデータがアドレスやサイズのデータなので、それをコピーして渡す。

 

じゃあ、C言語っぽく配列を渡したかったらどうするかというと、スライスにすればいいそうだ。
まあ、この辺は慣れるしかないな。


いかんいかん、ちゃんとスライスにすればOKなのかどうかを確認していなかった。

01: package main
02: 
03: import "fmt"
04: 
05: func gogo(bababa [5]int) {
06: 	for i := range bababa {
07: 		 bababa[i] = 10 + i
08: 	}
09: }
10: 
11: func gogo2(bababa []int) {
12: 	for i := range bababa {
13: 		 bababa[i] = 10 + i
14: 	}
15: }
16: 
17: func main() {
18: 	var momomo [5]int
19: 
20: 	gogo(momomo)
21: 	for i, val := range momomo {
22: 		fmt.Printf("gogo[%d] = %d\n", i, val)
23: 	}
24: 
25: 	gogo2(momomo[:])
26: 	for i, val := range momomo {
27: 		fmt.Printf("gogo2[%d] = %d\n", i, val)
28: 	}
29: }
  

$ go run ex4
gogo[0] = 0
gogo[1] = 0
gogo[2] = 0
gogo[3] = 0
gogo[4] = 0
gogo2[0] = 10
gogo2[1] = 11
gogo2[2] = 12
gogo2[3] = 13
gogo2[4] = 14

うむ、よかろう。

2020/02/14

[golang]スライスと配列。あるいは配列とスライス。

昨年の記憶からすると、golangのスライスと配列は宣言が似ているものの、同じように使おうとすると怒られる、という代物だった。

あまり考えず、思いつくままに書いてみよう。

01: package main
02: 
03: 
04: func gogo(bababa int[]) {
05: 	for i := 0; i < bababa.length; i++ {
06: 		bababa[i] = i
07: 	}
08: }
09: 
10: func main() {
11: 	var momomo int[];
12: 
13: 	momomo = new int[10];
14: 	gogo(momomo)
15: 	for i := 0; i < momomo.length; i++ {
16: 		fmt.Printf("[%d] = %d\n", i, momomo[i])
17: 	}
18: }
  

はい、コンパイルエラーです。

golangは型を後に書くタイプだったよなー、とか、Javaの配列は型名の後だったよなー、とか、そんなことを考えてやってみたのだが、やっぱりダメだった。

01: package main
02: 
03: import "fmt"
04: 
05: func gogo(bababa []int) {
06: 	for i := 0; i < len(bababa); i++ {
07: 		bababa[i] = i
08: 	}
09: }
10: 
11: func main() {
12: 	var momomo []int
13: 
14: 	momomo = make([]int, 5)
15: 	gogo(momomo)
16: 	for i := 0; i < len(momomo); i++ {
17: 		fmt.Printf("[%d] = %d\n", i, momomo[i])
18: 	}
19: }
  

 

型名の前にスライスであることを表すのだな。
そして、スライスの要素数はlen()。確保済みの要素数はcap()らしい。


for文で要素数の数だけループさせるというのは、最近の言語ではあまりやらん気がする。
でもforeachっぽい書き方をすると代入ができないんじゃなかろうか?

01: package main
02: 
03: import "fmt"
04: 
05: func gogo(bababa []int) {
06: 	for i, value := range(bababa) {
07: 		value = i
08: 	}
09: }
10: 
11: func main() {
12: 	var momomo []int
13: 
14: 	momomo = make([]int, 5)
15: 	gogo(momomo)
16: 	for i, val := range(momomo) {
17: 		fmt.Printf("[%d] = %d\n", i, val)
18: 	}
19: }
  

これはコンパイルエラーになった。
gogo()の方でvalueを使っていないという扱いになったからだ。

01: package main
02: 
03: import "fmt"
04: 
05: func gogo(bababa []int) {
06: 	for i := range bababa {
07: 		bababa[i] = i
08: 	}
09: }
10: 
11: func main() {
12: 	var momomo []int
13: 
14: 	momomo = make([]int, 5)
15: 	gogo(momomo)
16: 	for i, val := range momomo {
17: 		fmt.Printf("[%d] = %d\n", i, val)
18: 	}
19: }
  

これなら通った。

第2戻り値を使わないので、最初は

for i, _ := range bababa {

にしたのだが、これはコンパイルエラーではないものの、警告されてしまった。
省略するものらしい。

 

range(momomo)は、vscodeのフォーマッタを使ったら括弧を外されてしまった。
しかし、len(momomo)はダメらしい。

lenは関数で、rangeは関数ではないということだろうか?

https://golang.org/ref/spec#RangeClause

rangeはRnageClauseというものらしい。れんじくらうぜ?

なんでもかんでもrangeが使えるわけではない。array, slice, string, map, channel。
戻り値も、1stと2ndでちゃんと決められているそうだ。

rangeはfor文専用のようだ。
だからlenとは扱いが違うのだろう。

 

これで終わろうと思ったが、よくわからないサンプルがあった。

01: package main
02: 
03: import "fmt"
04: 
05: func gogo(bababa []int) {
06: 	for i := range bababa {
07: 		 bababa[i] = 10 + i
08: 	}
09: }
10: 
11: func lolo(bababa []int) {
12: 	i := 3
13: 	for i, bababa[i] = range bababa {
14: 		
15: 	}
16: }
17: 
18: func main() {
19: 	var momomo []int
20: 
21: 	momomo = make([]int, 5)
22: 	gogo(momomo)
23: 	lolo(momomo)
24: 	for i, val := range momomo {
25: 		fmt.Printf("[%d] = %d\n", i, val)
26: 	}
27: }
  

ちなみに、オリジナルはこうだ。

i = 2
x = []int{3, 5, 7}
for i, x[i] = range x {  // set i, x[2] = 0, x[0]
	break
}
// after this loop, i == 0 and x == []int{3, 5, 3}

 

breakがあってもなくても、なんか動作がよくわからない。
今日は眠たいので、また次回だ。

 

あ、配列のこと書き忘れた!
まあ、それも含めた次回だな。。

2020/02/12

[golang]goroutineをいくつか試す

golangで名前だけ知っていて、試していない機能がいくつもある。

今回はgoroutineをいくつか動かしてみる。


まず、取りあえず動かないこともないコードからいこう。

01: package main
02: 
03: import (
04: 	"fmt"
05: 	"time"
06: )
07: 
08: func gor1() {
09: 	fmt.Printf("gor1\n")
10: }
11: 
12: func main() {
13: 	go gor1()
14: 	time.Sleep(time.Second)
15: }
  

1秒もあればgoroutineの1つくらいは動くよね、というだけで、環境によってはfmt.Printf()が出力されなくてもおかしくはない。

 

関係ないけど、golangは前方参照だっけ?

> There are no forward declarations and no header files; everything is declared exactly once.
https://golang.org/doc/faq#principles

違うそうだ。
まあ、前方参照のつもりで書いていても問題ないだろう。


chanを使うと、その変数は通信チャネルになるらしい。

01: package main
02: 
03: import (
04: 	"fmt"
05: )
06: 
07: func gor1(channel chan int) {
08: 	fmt.Printf("gor1\n")
09: 	channel <- 0
10: }
11: 
12: func main() {
13: 	cn := make(chan int)
14: 	go gor1(cn)
15: 	<-cn
16: }
  

なんかもったいない使い方だが、まあ、普通ならgoroutineにしなくてよい関数をそうしているからだろう。

 

01: package main
02: 
03: import (
04: 	"fmt"
05: 	"time"
06: )
07: 
08: func gor1(channel1 chan int, channel2 chan int) {
09: 	fmt.Printf("gor1-1\n")
10: 	channel1 <- 0
11: 	time.Sleep(time.Second * 3)
12: 	fmt.Printf("gor1-2\n")
13: 	channel2 <- 0
14: }
15: 
16: func main() {
17: 	cn1 := make(chan int)
18: 	cn2 := make(chan int)
19: 	go gor1(cn1, cn2)
20: 	<-cn1
21: 	<-cn2
22: }
  

ふむ、複数chanでもいいのか。

あ、chanの待つ順番をcn2を先にするとどうなるだろう?

$ go run .
gor1-1
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
        /home/xxx/golang/test/src/ex2/ex2.go:20 +0xa5

goroutine 18 [chan send]:
main.gor1(0xc000062060, 0xc0000620c0)
        /home/xxx/golang/test/src/ex2/ex2.go:10 +0x7d
created by main.main
        /home/xxx/golang/test/src/ex2/ex2.go:19 +0x8e
exit status 2

デッドロックするんだ。
cn1は後でチェックすればいいだけかと思ったが、そういう問題でも無いのか?

Go言語—バッファなしのチャンネルに送受信するとデッドロックが起こる理由 - Qiita
https://qiita.com/YumaInaura/items/943c24ffb64df5e01c65

ほう、cn1がまだ受信の待ち受け状態じゃないのに送りつけたからダメ、ということか。

なので、cn2を1つだけバッファを持たせれば動く。

01: package main
02: 
03: import (
04: 	"fmt"
05: 	"time"
06: )
07: 
08: func gor1(channel1 chan int, channel2 chan int) {
09: 	fmt.Printf("gor1-1\n")
10: 	channel1 <- 0
11: 	time.Sleep(time.Second * 3)
12: 	fmt.Printf("gor1-2\n")
13: 	channel2 <- 0
14: }
15: 
16: func main() {
17: 	cn1 := make(chan int, 1)
18: 	cn2 := make(chan int)
19: 	go gor1(cn1, cn2)
20: 	<-cn2
21: 	<-cn1
22: }
  


chanは双方向のようだ。

01: package main
02: 
03: import (
04: 	"fmt"
05: 	"time"
06: )
07: 
08: func gor1(channel1 chan int, channel2 chan int) {
09: 	fmt.Printf("gor1-1: %d\n", <-channel1)
10: 	time.Sleep(time.Second * 3)
11: 	fmt.Printf("gor1-2\n")
12: 	channel2 <- 2
13: }
14: 
15: func main() {
16: 	cn1 := make(chan int)
17: 	cn2 := make(chan int)
18: 	go gor1(cn1, cn2)
19: 	fmt.Printf("main-1\n")
20: 	cn1 <- 1
21: 	fmt.Printf("main-2: %d\n", <-cn2)
22: }
  

同時に両方書き込むようなタイミングがあるようだったら、mutexみたいな排他処理をするのだろうか。
いろいろ種類がありそうなので、また今度調べよう。

2020/02/11

[golang]importやmainの分け方

golangのパスなどについて理解する方法を考えていたのだが、あきらめることにした。
機械的に覚えても困らないだろう。

 

まず、mainパッケージは専用ディレクトリを作って、そこに置く。
推奨構成があったと思うが、cmd/<コマンド名> ディレクトリの下に置くのがいいだろう。

そのディレクトリの中には、そのコマンドに関係するファイルだけを置くのがいいんじゃなかろうか。
src/ex1ディレクトリの下に、ex1.goとex1sub.goを置き、ex1.goはmain、ex1sub.goはpppというパッケージ名にしたのだが、

$ go run .
package ex1: found packages main (ex1.go) and ppp (ex1sub.go) in /home/xxx/golang/src/ex1

などと言われてしまった。

やり方はあるのかもしれんが、ex1sub.goもmainパッケージにすれば済むだけの話なのだ。
私の「あきらめる」というのは、そこら辺はもう考えないで済ませたい、という逃げの戦法なのだ。

 

それはともかく、1つの実行ファイルを作るのにはsrc以下にディレクトリを作り、その中でさらにコマンドを分けたいならcmdディレクトリを作ってその中で分ければいいだろう。

ライブラリのようにまとまった機能を持たせるなら、それはそれでディレクトリを別にして、そのディレクトリ内は同じパッケージ名を使うとよいだろう。
この辺はC#やJavaなどでも同じだろう。


書くことがなくなってしまった(深く欠けるほどの知識がたまってない)ので、goroutineのことでも書いておこう。

まあ、goroutineについても知識がないのだが、OSのスレッド(私はLinux環境なのでpthread)とは別物だ、ということはわかった。
調べてないけど、広義のスレッドではあると思う。

 

volatileのようなキーワードはなさそうだ。
chanというものがあるようだが、まだわかってない。
C/C++のvolatileは「最適化しないでコードで見える形にコンパイルしてねー」というお願い(命令)なので、最適化されるとまずそうなところに使っていくのだけど、golangでは並列実行を言語として組み込んでいるから変に考え込まずに正しく実装すれば済むようになっているのだろう。

最適化による失敗はわかりづらいし、下手するとパフォーマンスを落とすことになるから、言語に組み込まれたことでやりやすくなるのだろう。

一抹の寂しさを感じなくもないが、変なところで失敗しなくて済むという安心感の方が強いな。

[golang]go runの引数はなんなのか

実は、今ひとつgolangの基本的なことが分かっていない。

importで指定しているあれはなんなのか? や、go runの引数はなんなのか? ということすらもだ。

 

では、今はどうやって指定しているかというと、なんとなく、だ。
エラーになったら違う文字列にしてみよう、くらいで、あまり考えていない。
さすがにこれはまずい。。。

 

importも気になるのだが、今回はgo runだけにしておこう。


go runの引数。

--helpを見ると、こうだった。

usage: go run [build flags] [-exec xprog] package [arguments...]

はい、packageですね。

 

しかし、だ。

src/ex1/ex1.go

01: package main
02: 
03: import "fmt"
04: 
05: func main() {
06: 	fmt.Printf("Hello\n")
07: }
    

golang/src/ex1 $ go run ex1.go
Hello

ほら、ファイル名でも通るじゃないか。
むしろ、

golang/src/ex1 $ go run main
package main: cannot find package "main" in any of:
        /usr/lib/go-1.13/src/main (from $GOROOT)
         /home/ueno/golang/test/src/main (from $GOPATH)

mainパッケージを指定した方がエラーになるくらいだ。

つまり、mainパッケージはパッケージ名ではない、ということか?

golang $ go run src/ex1/ex1.go
Hello

$GOPATHからのファイルパスを直接書いても通る。

golang $ go run ex1
Hello

かと思えば、ディレクトリ名でも通る。

では、packageを変えてみる。

src/ex1/ex1.go

01: package main
02: 
03: import "fmt"
04: 
05: func main() {
06: 	fmt.Printf("Hello\n")
07: 	fmt.Printf("%s\n", sub())
08: }
    

src/ex1/ex1sub.go

01: package main
02: 
03: func sub() string {
04: 	return "Sub"
05: }
    

どちらもmainだ。

 

golang/src/ex1$ go run ex1.go
# command-line-arguments
./ex1.go:7:21: undefined: sub

ふむ、1つしかファイルがない場合は特例として認める、というところだろうか。
同じディレクト入りにmainパッケージのファイルが複数あっても、ファイル名を指定すれば良いというわけだ。

 

golang/src/ex1$ go run ex1sub.go
# command-line-arguments
runtime.main_main·f: function main is undeclared in the main package

エントリポイントがないファイルはダメ、と。

 

golang/src/ex1$ go run .
Hello
Sub

ディレクトリを指定したのと同じことか。

 

golang/src$ go run ex1
Hello
Sub

golang$ go run ex1
Hello
Sub

うーん、ディレクトリというよりも、srcの下をたどる?

 

golang$ go run src/ex1
package src/ex1: cannot find package "src/ex1" in any of:
        /usr/lib/go-1.13/src/src/ex1 (from $GOROOT)
        /home/xxx/golang/src/src/ex1 (from $GOPATH)

srcはパッケージ名の一部ではない、と。

 

えーい、正解を見よう。

https://golang.org/cmd/go/#hdr-Compile_and_run_Go_program

> Run compiles and runs the named main Go package.

パッケージ名というよりは、mainパッケージを含んでいるパッケージを指定する?

 

> Typically the package is specified as a list of .go source files from a single directory, but it may also be an import path, file system path, or pattern matching a single known package, as in 'go run .' or 'go run my/cmd'.

複数のgoファイルを含んだ単一のディレクトリ(importパス、systemパス)、パターンに一致する単一のパッケージなどが典型的だと。

 

パスでもいいからgoファイル名でもいいと言うことかな?
だいたい、エントリポイントはmainパッケージに置くものだから、パッケージ名限定にできるはずがなかったな。

しかし、使いやすくしようとして、返って分かりづらくなっただけのようにも感じる。
いっそのことJavaみたいにディレクトリ階層がpackageになっていてmainパッケージが必須じゃない方があきらめがついたかもしれない。

ディレクトリの指定を許可するなら、相対パスで書いてもよいようにすればよさそうだが、そこは推奨しないらしい。
でも、pkg/だったりsrc/だったりすると、なんだかもう訳がわからないのよねぇ。

 

golangに文句があるというよりは、ルールがよくわからないので解釈に困っているというところだ。
バージョンの最初の方から使っていたら理解しやすかったのだろうか。

[golang]vscodeの設定など

visual studio codeをgolang環境でも使いたい。
OSはLinuxというかLubuntu18.04、vscodeは1.42.0。

作業ごとにgolangのバージョンも分けたいし、GOPATHも変えたい。
となると、環境変数を変更した上でvscodeを起動せんといかん。

ビルドは手動でやりたいし、できれば大半の作業は手動でやりたい。
そうでないと、なんだかよくわからなくなるからだ。
慣れたら自動でやってもらうかもしれんが、まずは手動で制御するようにしたい。


まず、Microsoftのgo extensionをインストールした。

Tools Gopathを設定して、vscode専用のディレクトリを作った。
GOPATHをちょこちょこ変更するので、そのたびにvscode用のツールをビルドされても困るからだ。

Build on SaveをOFF。

最初の設定はこんなところか。

 

goファイルを開くと、ツールのインストールを求められたのでALLの方を選んだ。
こういうメッセージも出てきた。

image

Format Toolをgoimportsにし、goファイルを開いてimportを適当に削除してファイル保存する。
そうすると、自動的にimportが追加された。
しかし・・・自動で、というのが困る。

これは、go言語のみformat on saveを無効にすれば良いそうだ。
build、lint、vet(検査?)はgo言語の設定だが、formatはエディタの機能だからこうなるのか。
自動でformatしたい場合は、F1を押してFormat Documentすればよい。

 

でも、私はeditor.formatOnSaveを有効にしていないのだが、なぜフォーマットされてしまうのだろう?
settings.jsonで明示的にfalse指定しても、go言語指定でfalseにしないとフォーマットされてしまう。

The Go extension runs the below on each file save by default.

と書いているから、go extensionがそうしてしまうと考えるのが妥当か。
だが、非常に迷惑だ。extension以外の設定に効果を及ぼすextensionというのはまずいんじゃなかろうか。
チェックするのはいいのだが、自動整形したくない設定をしているのに成形されてしまうのは困る。

"editor.formatOnSave": false,  //これはなくてもよい
"[go]": {
    "editor.formatOnSave": false
},

まあ、golangが比較的、あれこれを強制的に決めてしまう言語だからというのもあるのかもしれんが、、、

 

いかんいかん、歳を取ると愚痴っぽくなってしまうな。
回避方法が分かっているのだから、まあよしとしよう。


次はGOPATHだ。
今回の意図からすると、.vscode/settings.jsonにgo.gopathを書くのが妥当なのだろう。

が、私はまだworkspaceに慣れていない。
そのせいで、srcの下でvscodeを開いたり、その上で開いたりと、かなり適当なのだ。
vscodeのworkspaceは開きたいファイルのリストのように使えるのだろうが、どうしても「開いたディレクトリ以下にあるファイルを開くもの」と思い込んでしまっているのだ。

 

というわけで、2段構えだ。
環境変数のGOPATHも設定し、、.vscode/settings.jsonにgo.gopathも書く。
vscodeは相対パスや環境変数が使えるのかよくわからないのだが、環境変数のGOPATHはデフォルトで使っていない(毎回設定している)のでコンソールを開くし、コンソールを開くならコマンドラインからvscodeを起動すれば良いだけだ。


とりあえず、こんなところだろうか。

仕事でgolangを使っているPCで、どういうコードを書けばいいか実験的に試すこともある。
そのときに、binやpkgが混ざらないようにしたいのだ。
go getでキャッシュなんかにダウンロードされているようだから、完全に分けるのは難しいだろうが、まあ、心意気だ。

2020/02/04

[golang]よく使うコマンド v1.13

ブログは久しぶりだが、golangも久しぶりだ。
歳を取るというのは困ったもので、コマンドが思い出せない・・・。

自分のためにメモを残そう。


環境変数

GOPATHだけ設定しておけば事足りる。

 

ディレクトリ

GOPATHの下に、bin, pkg, srcディレクトリを作れば良い。

作業場所は、srcの下になる。
githubなどで管理しないなら、srcの下に直接ディレクトリを掘って、そこで作業すれば良いだろう。
githubなどを使うなら、src/github.com/<githubのアカウント>/<githubのリポジトリ>、というディレクトリを作って、そこで作業すれば良いだろう。

 

go.mod

最近は、go.modを使うのがよさそうだ。
作業するディレクトリの一番上で、go mod init、でデフォルトのgo.modファイルができる。
あとはgo getすれば関連するパッケージなんかもダウンロードしてくれる。

が、go getは難しいコマンドだと思うのだよ、私は。
使うのは-dか-uくらいだと思う。
環境変数で動作が変わったりするから難しいと思うのだろうか。

"go get"だけだと、src/にダウンロードしているようだ。これはcacheを使っていないような感じがする。ただ、go.modファイルは使わないようだ。

"go get -u"は、go.modがないと動作しない。go.modがあればpkg/にダウンロード。キャッシュがあるならそれを使う?

"go get -d"は、src/にダウンロード。キャッシュがあるならそれを使う?

go.modがあると、キャッシュがあればそれを使っているように見える。終わるまでが早いからだ。

 

よくわからないのが、go.modの更新。
go get -u やgo get -d を使うと、go.modも更新されるようなのだ。go getを使うとdependencyを追加するようなことが書かれているので、オプション無しでも更新されていたのかもしれん。

 

clean

消すコマンドもある。

go cleanだと、たぶんカレントディレクトリでビルドしてあるものを削除してくれる。binまでは消えないのか。

go clean -modcache、で$GOPATH/pkgにあるmodの中を消してくれる。そうしないとsudoなどで消さないといけないこともあるのだ。

go clean -cacheは、~/.cache/go-build/ 以下にあるgo getで取得したキャッシュを消してくれる。

 

buildとrun

go buildとgo runがある。
パスを書くときとか、goファイル名を書くときとか、実はよく理解してない。。。
基本的には、mainがあるファイル名を指定しておけばいいんじゃないかなぁ。


 

こういう、自分でも後から見たい情報は、ブログだと探すのが面倒なのよねぇ。
scrapboxを仕事で使ってるけど、個人だと無料で使えるようなので、使ってみようかしら。

 

https://scrapbox.io/hiro9ma-93873243/golang_-_command