• 不能使用make创建一个结构体,会引发编译错误。使用new创建map且直接使用会导致运行时错误
    ( panic: assignment to entry in nil map)
  • 禁止结构体让别人调用的时候使用new创建
    将结构体以小写开头即可
  • range 不是一个函数, 无效语法range(xxx)
  • 不要使用 new,永远用 make 来构造 map
  • new和make的区别
    都是堆上分配内存。new等价于&T{}且返回的是类型为T值为0的指针。make(T)返回的是引用不是指针且只能适用于slice、map、channel
  • 以下两种方法生成相同的接口
make([]int, 50, 100)
new([100]int)[0:50]
  • 参数传递都是值拷贝。函数如果需要修改外部传入的参数只能使用指针。但是像切片(slice)、字典(map)、接口(interface)、通道(channel)这样的引用类型默认使用引用传递(即使没有显示的指出指针)传递的是指针,所以是可以直接传递给函数进行修改的。for i,v 方式的迭代注意也是值拷贝

  • a []int 是一个切片类型 a [...]是一个数组类型

  • 那么多中变量初始化语法,怎么理解

var a
a = 100
# error
var a = 10 # ok
var a int = 10 # ok
a := 10
a int = 10  # error

变量在声明时候,一定要让编译器知道是什么类别。var a这种写法,编译器不知道什么类别,var a = 10能够根据10推导为int类型。a int = 10, 冗余错误写法。
对于函数的参数的书写方法也是类似要求,必须知道参数属于什么类型。
你声明过的变量,不能够重复声明,这点可能会在函数使用时会遇到。

  • 函数返回值

在Go的函数中,返回值你不写变量名,你需要显示return。
写了变量名,会自动帮你return函数里面的变量。

  • *符号
    *在表示类型的时候是指针类型,在赋值的时候代表得到指针指向的值

  • 类型的实例指针变量

func (p *Person) ChangeAge(age int)  {
    fmt.Printf("ModifyAge")
    p.age = age
}

上面的方式的接收体是Person,通常我们会以实例的方式调用此方法,而不是指针

person.ChangeAge(10)

会自动变为

(&person).ChangeAge(10)
  • json不起作用
type User struct {
    age int
    name string
}

age和name是私有,需要改为大写。

type User struct {
    Age int `json:"age"`
    Name string `json:"name"`
}
  • &符号

在 Go 语言中,对结构体进行&取地址操作时,视为对该类型进行一次 new 的实例化操作。取地址格式如下

ins := &T{[arg]}

&在创建一个结构体对象的时候经常使用:

c.Data["json"] = &User{18, "张三"}

你也可以使用new关键字

ins := new(T)
  • 在函数里返回局部变量指针
func fun() *int { //int类型指针函数
    var a := 1
    return &a  //这里也返回了局部变量a的地址
}

如果在C语言,返回局部变量非指针是返回拷贝,是可以的。在C语言中返回局部变量指针是不可以的(加static提升变量到堆可以实现)。
在GO中,返回指针时候,会自动提升到堆。非指针返回拷贝。

When possible, the Go compilers will allocate variables that are local to a function in that function’s stack frame. However, if the compiler cannot prove that the variable is not referenced after the function returns, then the compiler must allocate the variable on the garbage-collected heap to avoid dangling pointer errors.

  • 下划线导包 匿名导包

如果只希望导入包,而不使用任何包内的结构和类型,也不调用包内的任何函数时,可以使用匿名导入包。

import (
    _ "path/to/package"
)

会调用package的init函数

  • 封装

GO没有类似private的关键字,私有,一般其它包不可用,引用名大写开头即可。

package mypkg
var myVar = 100
const MyConst = "hello"
type MyStruct struct {
}

myVar对于其他包不可用

  • 程序入口

程序入口是包名为main的main函数

  • 找不到GOPATH

在beego会遇到,但是go env能找到环境变量。需要再次export GOPATH。

  • 继承

继承使用的是组合的面向对象编程思想。和c结构体继承实现类差不多。
只需要在结构体里面写其它结构体即可继承

  • 多态

可以继承一个接口,子类实现接口。再声明接父类引用指向子类。
对于继承自结构体的,不能直接父类引用指向子类对象。但是,可以使用变量类型自动推导提高代码复用性。

  • 字符串拼接

字符串之间使用+号可以拼接,对于int类型的,需要使用strconv

import `strconv`
a := 10
b := strconv.Itoa(a)
s := "string s"
d := b + s
  • 如何将函数作为形参
func visit(list []int, f func(int)) {
  ...
}
  • no new variables on left side of :=
func exec_shell(cmd string) (output []byte, err error)  {
    arr := strings.Split(cmd, " ")
    command := exec.Command(arr[0], arr[1:len(arr)]...)
    output2, err2 := command.Output()
    return output2, err2
}

在返回值写了变量名,相当于声明了一次了。所以是output2, err2而不是output, err
下面代码也是可以的

func exec_shell(cmd string) (output []byte, err error)  {
    arr := strings.Split(cmd, " ")
    command := exec.Command(arr[0], arr[1:len(arr)]...)
    output, err = command.Output()
    return
}
  • 导包没有使用、定义变量没有使用

如果没有使用的话,会编译失败。这是Go强制的语法要求

  • if语句作用域
    if true {
        var int a = 100
    }
    print(a)

上面的代码在go,c都是错误的。
和python不同,但是和c一样,大括号if语句是有作用域的。

  • 不定参数传递数组
arr := strings.Split(cmd, " ")
command := exec.Command(arr[0], arr[1:len(arr)]...)

需要在参数后面添加...

  • channel在同一个goroutine使用
func main() {
    // 并发执行程序
    ch := make(chan int)
    ch 

第四行,会阻塞,导致第六行无法执行。在main线程不能这么使用。会报fatal error: all goroutines are asleep - deadlock!错误。当然,你可以设置chan的缓冲区大小大于1,就不会报错。

  • 接收多个通道的数据

思考以下部分代码

a := 

b如果要赋值,必须等到a赋值完成,那如果我们想同时呢?
使用for

for{
    // 尝试接收ch1通道
    data, ok := 

最好的选择是使用select

select{
    case 操作1:
        响应操作1
    case 操作2:
        响应操作2
    …
    default:
        没有操作情况
}
  • 数组和切片
[3]int{1,2,3},//array 数组,确定数组长度
[...]int{1,2,3},//array 数组,由编译器自动计算数组长度

上面两个都属于数组,定长内存。数组引用赋值会导致拷贝。数组不能使用append。

[]int{},// slice 切片
[]int{1,2,3},// slice 切片
[]int{1,2,3}[:],//切片再切还是切片
make([]int,3,10),//标准的slice 定义方式

切片里面是数组,但是当元素数量大于数组长度,会自动扩容。append操作之后的返回的切片指向会改变。

  • 接口赋值错误xxx method has pointer receiver 以及接口类型的形参是使用指针还是实例
package main

import (
    "fmt"
)

type Animal interface {
    Eat()
}


type Student struct {
    Name string
    Age int
}

func (s *Student) study () error {
    fmt.Println("学生在学习")
    return nil
}

func (s *Student) Eat () {
    fmt.Println("eat ...")
}

func main() {
    fmt.Println("hello world")
    var interPerson Animal
    var stu Student = Student{"lisi", 18}
        interPerson = stu // 报错
    interPerson = &stu
    interPerson.Eat()
}

interPerson = stu赋值时会报错,因为func (s *Student) Eat ()接受者是指针类型,你传递的实例引用。
但是,你把func (s *Student) Eat ()写成“func (s Student) Eat () `,那么

interPerson = stu
interPerson = &stu

都可以运行。也就是说,你传递指针会自动转化为实例。
这里就和普通实例调用接收者为指针类型又不一样了。一般结构体方法调用,会自动帮你转换,不管接收者是什么类型。
对于接口的赋值,接受者是指针,你就赋值指针,是实例,你可指针也可以实例。所以大部分时候,函数的形参如果是,接口类型不应该写指针类型,而是写实例

  • 接口类型转化
an, ok := interPerson.(Animal)
an.Eat()

可以使用interface.(TYPE)实现将TYPE转化为interface接口指定类型。

  • 匿名组合和匿名指针组合
    结构体匿名组合在创建对象的时候,不需要传递结构体引用。
    匿名指针结构体,创建的时候需要赋值一个结构体的指针。

  • 切片索引问题

    arr := make([]int, 5, 10)
    arr[5] = 9

上面的代码会报错,下标越界,切片不允许索引比超过切片长度的数据。
你可以用append来操作

    items := make([]map[string]int, 5, 10) // map切片里面有5个元素 容量为10
    items = append(items, make(map[string]int, 1))
  • 如何创建func map
funcMap := make(map[string]func() int)
  • 最小作用域
    和python不同,最小作用域是函数,go的是大括号
    {
        b := 100
    }
    fmt.Println(b)

上代码和短声明错用结合一起,编译不会报错

    a := 99

    if a 

外部的a永远也不会改变。

  • 切片什么时候会越界
    1 当以slice[i]这种方式取的时候,i不能大于等于len(slice)
    2 当slice[start:end]的时候 end不能大于cap(slice)

  • 定义一个函数类型的类型,我应该怎么赋值

package main

import "fmt"

type myStr string

type myFun func(string)

func foo(str myStr) {
    fmt.Println("foo", str)
}

func bar(fun myFun, arg string){
    fun(arg)
}

func myPrint(arg string) {
    fmt.Println("start logging")
    fmt.Println(">>>>", arg)
    fmt.Println("end logging")
}

func main()  {
    foo("zhangsan")

    // 函数类型的 类型体 赋值 直接赋值为函数引用
    var testFun myFun;
    testFun = myPrint
    arg := "hello world!"
    bar(testFun, arg)
}
  • go run xxx.go 出现变量或函数未定义
    go run 默认只会将xxx.go 文件和其代码import的包进行加载。如果你导入的是相同main包下的不同文件的变量或者函数,那么就会出现此问题。使用go run xxx.go xxxa.go 或者 go run .解决

文章来源于互联网,如有雷同请联系站长删除:Go 语法入坑笔记

发表评论