Golang奇葩点

本文即Go语言的那些坑二。git

Golang中函数被看作是值,函数值不能够比较,也不能够做为map的key

请问如下代码能编译经过吗?github

import (
	"fmt"
)

func main(){
	array := make(map[int]func ()int) array[func()int{ return 10}()] = func()int{
		return 12
	}

	fmt.Println(array)
}``` **答案:** 复制代码

能够正常编译经过。数组

稍做改动,改成以下的状况,还能编译经过吗?

​```Go
import (
	"fmt"
)

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

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

	fmt.Println(array)
}```
**答案:**
复制代码

不能编译经过。bash

在Go语言中,函数被看作是第一类值:(first-class values):函数和其余值同样,能够被赋值,能够传递给函数,能够从函数返回。也能够被当作是一种“函数类型”。例如:有函数``func square(n int) int { return n * n }``,那么就能够赋值``f := square``,并且还能够``fmt.Println(f(3))``(将打印出“9”)。
Go语言函数有两点很特别:
+ 函数值类型不能做为map的key
+ 函数值之间不能够比较,函数值只能够和nil做比较,函数类型的零值是``nil``

# 匿名函数做用域陷阱

请看下列代码输出什么?

​```Go
import (
	"fmt"
)

func main(){
	var msgs []func()
	array := []string{
		"1", "2", "3", "4",
	}

	for _, e := range array{

			msgs = append(msgs, func(){
			fmt.Println(e)
		})
	}

	for _, v := range msgs{
		v()
	}
}
复制代码

答案:微信

4
4
4
4
复制代码

在上述代码中,匿名函数中记录的是循环变量的内存地址,而不是循环变量某一时刻的值。app

想要输出一、二、三、4须要改成:函数

import (
	"fmt"
)

func main(){
	var msgs []func() array := []string{
		"1", "2", "3", "4",
	}

	for _, e := range array{
		elem := e
		msgs = append(msgs, func(){
			fmt.Println(elem)
		})
	}

	for _, v := range msgs{
		v()
	}
}
复制代码

其实就加了条elem := e看似多余,其实不,这样一来,每次循环后每一个匿名函数中保存的就都是当时局部变量elem的值,这样的局部变量定义了4个,每次循环生成一个。ui

[3]int[4]int 不算同一个类型

请看一下代码,请问输出true仍是falsethis

import (
    "fmt"
    "reflect"
)

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

复制代码

答案是:spa

false
复制代码

数组长度是数组类型的一个组成部分,所以[3]int和[4]int是两种不一样的数组类型。

数组还能够指定一个索引和对应值的方式来初始化。

例如:

import (
    "fmt"
)

func main(){
    arrayA := [...]int{0:1, 2:1, 3:4}
    fmt.Println(arrayA)
}
复制代码

会输出:

[1 0 1 4]
复制代码

有点像PHP数组的感受,可是又不同:arrayA的长度是多少呢?

import (
    "fmt"
)

func main(){
    arrayA := [...]int{0:1, 2:1, 3:4}
    fmt.Println(len(arrayA))
}
复制代码

答案是:

4
复制代码

没错,定义了一个数组长度为4的数组,指定索引的数组长度和最后一个索引的数值相关,例如:r := [...]int{99:-1}就定义了一个含有100个元素的数组r,最后一个元素输出化为-1,其余的元素都是用0初始化。

不能对map中的某个元素进行取地址&操做

a := &ages["bob"] // compile error: cannot take address of map element
复制代码

map中的元素不是一个变量,不能对map的元素进行取地址操做,禁止对map进行取地址操做的缘由多是map随着元素的增长map可能会从新分配内存空间,这样会致使原来的地址无效

当map为nil的时候,不能添加值

func main() {
    var sampleMap map[string]int
    sampleMap["test"] = 1
    fmt.Println(sampleMap)
}
复制代码

输出报错:

panic: assignment to entry in nil map
复制代码

必须使用make或者将map初始化以后,才能够添加元素。

以上代码能够改成:

func main() {
    var sampleMap map[string]int
    sampleMap = map[string]int {
        "test1":1,
    }
    sampleMap["test"] = 1
    fmt.Println(sampleMap)
}
复制代码

能够正确输出:

map[test1:1 test:1]
复制代码

&dilbert.Position(&dilbert).Position是不一样的

&dilbert.Position至关于&(dilbert.Position)而非(&dilbert).Position

请看例子:

请问输出什么?

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)

}
复制代码

输出:

0xc42006c220
复制代码

输出的是内存地址

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

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)

}
复制代码

输出:

123
复制代码

Go语言中函数返回的是值的时候,不能赋值

请看下面例子:

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
}
复制代码

请问能编译经过吗?

运行,输出报错:cannot assign to EmployeeByID(1).Salary

在本例子中,函数EmployeeById(id int)返回的是值类型的,它的取值EmployeeByID(1).Salary也是一个值类型;值类型是什么概念?值类型就是和赋值语句var a = 1var a = hello world等号=右边的1Hello world是一个概念,他是不可以被赋值的,只有变量可以被赋值。

修改程序以下:

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
}
复制代码

这就能够编译经过了

在声明方法时,若是一个类型名称自己就是一个指针的话,不容许出如今方法的接收器中

请看下面的例子,请问会编译经过吗?

import (
	"fmt"
)

type littleGirl struct{
	Name string
	Age int
}

type girl *littleGirl

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

func main(){
	littleGirl := girl{Name:"Rose", Age:1}
	
	girl.changeName("yoyo")
	fmt.Println(littleGirl)
}

复制代码

答案:

不能编译经过,会提示“invalid receiver type girl(girl is a pointer type)”
复制代码

Go语言中规定,只有类型(Type)和指向他们的指针(*Type)才是可能会出如今接收器声明里的两种接收器,为了不歧义,明确规定,若是一个类型名自己就是一个指针的话,是不容许出如今接收器中的。

函数容许nil指针做为参数,也容许用nil做为方法的接收器

请看下面的例子,请问能编译经过吗?

import (
	"fmt"
)

type littleGirl struct{
	Name string
	Age int
}


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

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

	little = nil
	little.changeName("yoyo")
	fmt.Println(little)
}
复制代码

答案:

不能编译经过,显示"cannot use nil as type littleGirl in assignment"
复制代码

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的诞生时间。

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
复制代码

更多精彩内容,请关注个人微信公众号 互联网技术窝 或者加微信共同探讨交流:

相关文章
相关标签/搜索