Go語言的那些坑二

熱烈歡迎你,相識是一種緣分,Echa 哥為了你的到來特意準備了一份驚喜,go學習資料《 》

目錄:


Go語言的那些坑二


Golang中函數被看做是值,函數值不可以比較,也不可以作為map的key

請問以下代碼能編譯通過嗎?

<code>import (
\t"fmt"
)

func main(){
\tarray := make(map[int]func ()int)

\tarray[func()int{ return 10}()] = func()int{
\t\treturn 12
\t}

\tfmt.Println(array)
}/<code>

答案:

<code>可以正常編譯通過。/<code>

稍作改動,改為如下的情況,還能編譯通過嗎?


<code>import (
\t"fmt"
)

func main(){
\tarray := make(map[func ()int]int)

\tarray[func()int{return 12}] = 10

\tfmt.Println(array)

}/<code>


答案:

<code>不能編譯通過。/<code>

在Go語言中,函數被看做是第一類值:(first-class values):函數和其他值一樣,可以被賦值,可以傳遞給函數,可以從函數返回。也可以被當做是一種“函數類型”。例如:有函數func square(n int) int { return n * n },那麼就可以賦值f := square,而且還可以fmt.Println(f(3))(將打印出“9”)。Go語言函數有兩點很特別:

  • 函數值類型不能作為map的key
  • 函數值之間不可以比較,函數值只可以和nil作比較,函數類型的零值是nil

匿名函數作用域陷阱

請看下列代碼輸出什麼?


<code>import (
\t"fmt"
)

func main(){
\tvar msgs []func()
\tarray := []string{

\t\t"1", "2", "3", "4",
\t}

\tfor _, e := range array{

\t\t\tmsgs = append(msgs, func(){
\t\t\tfmt.Println(e)
\t\t})
\t}

\tfor _, v := range msgs{
\t\tv()
\t}
}/<code>


答案:

<code>4
4
4
4/<code>

在上述代碼中,匿名函數中記錄的是循環變量的內存地址,而不是循環變量某一時刻的值。

想要輸出1、2、3、4需要改為:

<code>import (
\t"fmt"
)

func main(){
\tvar msgs []func()
\tarray := []string{
\t\t"1", "2", "3", "4",
\t}

\tfor _, e := range array{
\t\telem := e
\t\tmsgs = append(msgs, func(){
\t\t\tfmt.Println(elem)
\t\t})
\t}


\tfor _, v := range msgs{
\t\tv()
\t}
}/<code>

其實就加了條elem := e看似多餘,其實不,這樣一來,每次循環後每個匿名函數中保存的就都是當時局部變量elem的值,這樣的局部變量定義了4個,每次循環生成一個。

[3]int 和 [4]int 不算同一個類型

請看一下代碼,請問輸出true還是false

<code>import (
"fmt"
"reflect"
)

func main(){
arrayA := [...]int{1, 2, 3}
arrayB := [...]int{1, 2, 3, 4}
fmt.Println(reflect.TypeOf(arrayA) == reflect.TypeOf(arrayB))
}/<code>

答案是:

<code>false/<code>

數組長度是數組類型的一個組成部分,因此[3]int和[4]int是兩種不同的數組類型。

數組還可以指定一個索引和對應值的方式來初始化。

例如:


<code>func main() {
var sampleMap map[string]int
sampleMap = map[string]int {
"test1":1,
}
sampleMap["test"] = 1
fmt.Println(sampleMap)
}/<code>

會輸出:


<code>map[test1:1 test:1]/<code>

有點像PHP數組的感覺,但是又不一樣:arrayA的長度是多少呢?


<code>map[test1:1 test:1]/<code>

答案是:


<code>map[test1:1 test:1]/<code>

沒錯,定義了一個數組長度為4的數組,指定索引的數組長度和最後一個索引的數值相關,例如:r := [...]int{99:-1}就定義了一個含有100個元素的數組r,最後一個元素輸出化為-1,其他的元素都是用0初始化。

不能對map中的某個元素進行取地址&操作


<code>map[test1:1 test:1]/<code>

map中的元素不是一個變量,不能對map的元素進行取地址操作,禁止對map進行取地址操作的原因可能是map隨著元素的增加map可能會重新分配內存空間,這樣會導致原來的地址無效

當map為nil的時候,不能添加值


<code>map[test1:1 test:1]/<code>

輸出報錯:


<code>map[test1:1 test:1]/<code>

必須使用make或者將map初始化之後,才可以添加元素。

以上代碼可以改為:


<code>map[test1:1 test:1]/<code>

可以正確輸出:

<code>map[test1:1 test:1]/<code>

&dilbert.Position和(&dilbert).Position是不同的

&dilbert.Position相當於&(dilbert.Position)而非(&dilbert).Position

請看例子:

請問輸出什麼?

<code>func main(){
type Employee struct {
ID int
Name string
Address string
DoB time.Time
Position string
Salary int
ManagerID int
}
var dilbert Employee

dilbert.Position = "123"

position := &dilbert.Position
fmt.Println(position)

}/<code>

輸出:

<code>0xc42006c220/<code>

輸出的是內存地址

修改一下,把&dilbert.Position改為(&dilbert).Position

<code>func main(){
type Employee struct {
ID int
Name string
Address string
DoB time.Time
Position string
Salary int
ManagerID int
}
var dilbert Employee

dilbert.Position = "123"

position := &dilbert.Position
fmt.Println(position)

}/<code>

輸出:

<code>123/<code>

Go語言中函數返回的是值的時候,不能賦值

請看下面例子:

<code>type Employee struct {
ID int
Name string
Address string
DoB time.Time
Position string
Salary int
ManagerID int
}

func EmployeeByID(id int) Employee {
return Employee{ID:id}
}

func main(){
EmployeeByID(1).Salary = 0
}/<code>

請問能編譯通過嗎?

運行,輸出報錯:cannot assign to EmployeeByID(1).Salary

在本例子中,函數EmployeeById(id int)返回的是值類型的,它的取值EmployeeByID(1).Salary也是一個值類型;值類型是什麼概念?值類型就是和賦值語句var a = 1或var a = hello world等號=右邊的1、Hello world是一個概念,他是不能夠被賦值的,只有變量能夠被賦值。

修改程序如下:

<code>type Employee struct {
ID int
Name string

Address string
DoB time.Time
Position string
Salary int
ManagerID int
}

func EmployeeByID(id int) Employee {
return Employee{ID:id}
}

func main(){
var a = EmployeeByID(1)
a.Salary = 0
}/<code>

這就可以編譯通過了

在聲明方法時,如果一個類型名稱本身就是一個指針的話,不允許出現在方法的接收器中

請看下面的例子,請問會編譯通過嗎?

<code>import (
\t"fmt"
)

type littleGirl struct{
\tName string
\tAge int
}

type girl *littleGirl

func(this girl) changeName(name string){
\tthis.Name = name
}

func main(){
\tlittleGirl := girl{Name:"Rose", Age:1}
\t
\tgirl.changeName("yoyo")
\tfmt.Println(littleGirl)
}/<code>

答案:

<code>不能編譯通過,會提示“invalid receiver type girl(girl is a pointer type)”/<code>

Go語言中規定,只有類型(Type)和指向他們的指針(*Type)才是可能會出現在接收器聲明裡的兩種接收器,為了避免歧義,明確規定,如果一個類型名本身就是一個指針的話,是不允許出現在接收器中的。

函數允許nil指針作為參數,也允許用nil作為方法的接收器

請看下面的例子,請問能編譯通過嗎?


<code>import (
\t"fmt"
)

type littleGirl struct{
\tName string
\tAge int
}


func(this littleGirl) changeName(name string){
\tfmt.Println(name)
}

func main(){
\tlittle := littleGirl{Name:"Rose", Age:1}

\tlittle = nil
\tlittle.changeName("yoyo")
\tfmt.Println(little)
}/<code>

答案:

<code>不能編譯通過,顯示"cannot use nil as type littleGirl in assignment"/<code>

Go語言中,允許方法用nil指針作為其接收器,也允許函數將nil指針作為參數。而上述代碼中的littleGirl不是指針類型,改為*littleGirl,然後變量little賦值為&littleGirl{Name:"Rose", Age:1}就可以編譯通過了。並且,nil對於對象來說是合法的零值的時候,比如map或者slice,也可以編譯通過並正常運行。

Golang的時間格式化

不同於PHP的date("Y-m-d H:i:s", time()),Golang的格式化奇葩的很,不能使用諸如Y-m-d H:i:s的東西,而是使用2006-01-02 15:04:05這個時間的格式,請記住這個時間,據說這是Golang的誕生時間。


<code>time := time.Now()

time.Format("20060102") //相當於Ymd

time.Format("2006-01-02")//相當於Y-m-d

time.Format("2006-01-02 15:04:05")//相當於Y-m-d H:i:s

time.Format("2006-01-02 00:00:00")//相當於Y-m-d 00:00:00/<code>


分享到:


相關文章: