golang 變數型態雜記
雜記
數字型態
- 如果使用二進制檔格式、網路協定…etc 那就使用對應的整數格式例如byte -> uint8
- 如果在寫lib,可能需要寫上多個不同整數格式
- 其他:大部分情況用默認
int
- 計算金額時因為golang跟大多數語言一樣使用IEEE754規格存放浮點數,計算上精準不足,建議使用
decimal
lib.
字串
- default value is string.empty.
- is immutable.
- for compiler, rune equals int32.
型別轉換
golang不提供自動型態轉換(automatic type promotion),例如c-sharp可以透過內建或自訂的implict,explict operator做自動的型別轉換,但golang不行。
好處是不用去記憶那些型別轉換規則,也不用去擔心精度喪失;缺點是這些東西都得自己handle.
變數宣告
1 | // 最完整 |
短宣告在package-level無法使用。
在function內如果宣告default值建議用:var x int
而非短宣告方式(option, coding style)
未定型態變數
未定型態變數在無法推斷確切型態的時候會給一個預設型態,通常未定型態變數比較彈性
1 | const x = 10 |
以上通通合法。
Array
宣告
1
2
3
4
5
6
7
8
9
10
11
12// 包含3個integer的array,值都為default , 0
var x [3]int
// 宣告同時賦值
var x [3]int{2,3,4}
// 稀疏陣列
var x [11]int{1,5:4,11:100}
// [1,0,0,0,4,0,0,0,0,0,100]
// 不定義大小
var x [...]int{2,3,4}golang不支援N維陣列,只能用模擬的
1
var x [5][5]int
代表有五個
[5]int
。不同大小的陣列被視為不同的型態,例如
[5]int
!=[3]int
,而型態在編譯期就會確定,所以無法將大小當作變數傳入陣列中。且無法使用型態轉換將大小不同的陣列轉換成一樣的大小:所以除非知道陣列的真正大小不然平常陣列用到的機會不多。
Slice
宣告,slice不用宣告大小,大小並非型態的一部分。
1 | var x = []int{1,2,3} |
append function
1 | var x []int |
capacity
slice有容量限制,當加入的值大過容量限制則go runtime會自動擴展這個slice,它會將slice的內的值複製到新的slice,將新的值加到結尾然後回傳新的slice,這樣的操作是肯定會消耗一點效率的。
所以在知道明確大小的情況下(或者可以預估最大大小的情況下)可以用make
函式指定容量
1 | x := make([]int, 5) |
但這個操作建立的是長度為5且容量為5的slice,預設的值為0,當新增值的時候會將長度擴展為6,容量翻倍(容量1024下預設翻倍,後續每次增加25%)
1 | x.append(x,6) |
如果要實現這個效果可以這樣用:
1 | x := make([]int, 0 ,5) |
x := make([]int, 0 ,5)
可以產生長度為0,容量為5的slice.
append !一!定! 會增加長度
切割
1 | x := []int{1, 2, 3, 4} |
slice與切割出來的sub-slice共用記憶體,換言之對sub-slice值做改變一樣會影響到main-slice,而切割出來的slice的容量與main-slice是一樣的。
因為共用記憶體,同理在對sub-slice做append的時候也會影響到main-slice.
比較特殊的作法是在切slice的時候同時把容量定成切出來的大小,再做append的時候因為超出容量所以會回傳一個容量更大的copy slice,就不會影響到main-slice.
1 | x := []int{1,2,3,4} |
但更好的作法我覺得用copy會更漂亮
1 | x := []int{1, 2, 3, 4} |
也可以部份複製
1 | x := []int{1, 2, 3, 4} |
String, rune, byte.
從string的源碼註釋可以看到
string is the set of all strings of 8-bit bytes, conventionally but not
necessarily representing UTF-8-encoded text. A string may be empty, but
not nil. Values of string type are immutable.
其實作法跟c-sharp差不多,由byte陣列組成string
golang
1 | var s string = "Test" |
c-sharp
1 | var s = "string"; |
一樣可以跟slice做切片運算。
var s2 string = s[:2]
而且字串是不可變的,它沒有slice那種修改sub影響main的問題,但它也有其他的問題;例如有些符號或emoji佔了多個byte,當然中文也是,因此字串切可能切不完整。
也因為這個原因,golang提供了rune
去做字串的處理。
1 | var s string = "哈囉,golang" |
比較[]byte, []rune就可以發現差異。
MAP
1 | var nilMap map[string]int |
map跟slice有多個相同的點
- map同slice會在容量不足時自動擴展
- 可以用make function建造自定義容量的map
- default value為nil(指的是map本身)
- map call by ref, 無法直接用
'=='
做比較
增刪改查map
1 | var shopItem map[string]int |
當map搜尋不到指定的key時會回傳0,在不確定0是找不到值還是值就是0的狀況下,可以使用ok
寫法。
1 | v, ok := shopItem["Whisky"] |
刪除特定值可以使用delete function,如果key不存在則不發生任何事情。
1 | delete(shopItem, "Whisky") |
Java跟C#語言有的Set
結構則golang並不提供,但可以用map模擬Set
結構,key值(index value)可以放欲放入set的值,而value則放boolean值,給定true。
1 | mockSet := make(map[int]bool) |
也用放空struct的方式,可以進一步降低記憶體使用量,但再取值就非得使用ok
寫法了。差別只有給定boolean會消耗 1 byte…
Struct
大多數語言都有的功能,但golang會用的比較重一點,golang並沒有class。
1 | type Student struct { |
匿名struct
1 | var student struct { |
可以在一些序列化的情況下用,譬如轉json。有點像是c#的dynamic variable.
struct 一般不可比較,除非他有相同的feild, 相同的順序,且都是call by value.
golang 變數型態雜記