其他分享
首页 > 其他分享> > Go 语言陷阱

Go 语言陷阱

作者:互联网

1、多值赋值和短变量声明

(1)相同类型的交量可以在末尾带上类型,例:var x , y int = 1 , 2。
(2)如果不带类型编译器,则可以直接进行类型推断,例:var x, y = 1 ,”tata ”。
(3)不同类型的交量声明和隐式初始化可以使用如下语法:
		var {
				x int
				y string
		}
(4)多位赋值语句中每个变量后面不能都带 类型,例:var x int, y string = 1 ,”ta ta "。
(5)右边是一个返回多值的表达式, 可以是返回多值的函数调用,也可以是 range map slice 等函数 作, 还可以是类型断言。
(6)赋值的左边操作数和右边的单一返回值的表达式的个数一样,逐个从左向右依次对左边的操作数赋值。
(7)赋值的左边操作数和右边的单 返回值的表达式的个数一样,逐个从左 向右依次对边的操作数赋值。

多值赋值语义含义:
(1))对左侧操作数中的表达式、索引值进行计算和确定,首先确定左侧的操作数的地址;然后对右侧的赋值表达式进行计算,如果发现右侧的表达式计算引用了左侧的变量,则创建临时变量进行值拷贝,最后完成计算。
(2)从左到右的顺序依次赋值。

2、短变量的声明和赋值(a , b := va , vb)

(1)使用“ := ”操作符 变量的定义和初始化同时完成。
(2)变量名后不要跟任何类型名, 编译器完全靠右边的值进行推导。
(3)支持多值短变量声明赋值。
(4)只能用在函数和类型方法的 部。
(5),在多值短变量声明和赋值时, 至少有一个变量是新创建的局部变量,其他的变量可以复用以前的变量,不是新创建的变量执行的仅仅是赋值。

3、赋值操作符“=”和“:=”的区别:

(1)“=”不会声明并创建新变 ,而是在当前赋值语句所在的作用域由内向外逐个去搜寻变量,如果没有搜索到相同变量名,则报编译错误。
(2)“:=”必须出现在函数或类型方法内部。
(3)“:=”至少要创建一个局部变量并初始化。

4、大多情况下 fo 循环块里的代码是在同一个 oroutine 里运行的,为了避免空间的浪费和 GC 的压力,复用了 range 是代临时变量。语言使用者明白这个规约,在for 循环下调用并发时要复制造代变量后再使用,不要直接引用 for 迭代变量。
5、defer陷阱(带 defer 的函数返回整体上有三个步骤)

(1)执行 return 的值拷贝,将 return 语句返回的值复制到函数返回值战区(如果只有一个return ,不带任何变量或值,则此步骤不做任何动作)。
(2)执行 defer 吾句,多个 defer按照 FILO 顺序执行。
(3)执行调整RET指令。

6、切片困惑

(1)数组:数组是有固定个相同类型元素的数据结构,底层采用连续的内存空间存放,数组一旦声明后大小就不可改变了。
(2)数组的一切传递都是值拷贝,体现在 以下几个方面:
	i. 数组间的直接赋值。
	ii. 数组作为函数参数。
	iii. 数组内嵌到 struct中。

7、make([]int,O va a []int 建的切片是有区别的 前者的切片指针有分配,后者的内部指
针为0。
8、多个切片引用同一个底层数组引发的混乱:切片可以由数组 建,一个底层数组可以创建多个切片,这些切片共享底层数组,使用append 扩展切片过程中可能修改底层数组的元素,间接地影响其他切片的值,也可能发生数组复制重建 共用底层数组的切片,由于其行为不明朗 不推荐使用。
9、多个切片共享一个底层数组,其中一个切片的 append 操作可能引发如下两种情况。

(1)append 追加的元素没有超过底层数组的容量,此种 append 操作会直接操作共享的底层数组,如果其他切片有引用数组被覆盖的元素,则会导致其他切片的值 隐式地发生变化。
(2)append 追加的元素加上原来的元素如果超出底层数组的容 ,则此种 append 操作会
重新申请新数组,并将原来数组值复制到新数组。
由于有这种 义性,所以在使用切片的过程中应该尽量避免多个切面共享底层数组, 可以
使用 co py 进行显式的复制。

10、Go 只有一种参数传递规则,那就是值拷贝,这种规则包括两种含义:

(1)函数参数传递时使用的是值拷贝。
(2)实例赋值给接口变量,接口对实例的引用是值拷贝。
(3)有时在明明是值拷贝的地方,结果却修改了变量的内容,有以下两种情况:i. 直接传递的是指针。指针传递同样是值拷贝,但指针和指针副本的值指向的地址是同一个地方,所以能修改实参值。ii. 参数是复合数据类型,这些复合数据类型内部有指针类型的元素,此时参数的值拷贝并不影响指针的指向。

11、Go 复合类型中 chan map slice interface 内部都是通过指针指向具体的数据,这些类型,的变量在作为函数参数传递时,实际上相当于指针的副本 。
12、comma,ok 表达式小结

(1)获取 map值:获取 map 中不存在键的值不会发生异常,而是会返回值类型的零值,如果想确定 map 中是否存在 key ,则可以使用获取 map 值的 comma,ok 语法。例如:v , ok := m[ ” some ” ]
(2)读取 chan 值:读取己经关闭的通道,不会阻塞,也不会引起 panic ,而是一直返回该通道的值。怎么判断通道已经关闭?有两种方法, 种是读取通道的 comma,ok 表达式,如果通道己经关闭,则
ok 的返回值是 fas le 另一种就是通过 range 循环迭代。示例如下:v , ok : = <- c。
(3)类型断言:re , ok : = body.(io.ReadCloser)

13、包中的函数或方法设计

(1)很多包的开发者会在内部实现两个“同名”的函数或方法,一个首字母大写 用于导出 API供外部调用 一个首字母 写,用于实现具体逻辑。一般首字母大写的函数调用首宇母 写的函数,同时包装一些功能 首字母小写的函数负责更多的底层细节。
(2)大部分情况下我 不需要两个同名且只是首字母大小写不同的函数,只有在函数逻辑很复杂,而且函数在包的 内、外部都被调用 的情况下,才考虑拆分为两个函数进行实现。一方面减少单个函数 复杂性,另一方面进行调用隔离。
(3)多值返回函数里如果有 error或者 bool 类型的返回值,则应该将 error或者 bool 作为最后一个返回值。

标签:变量,函数,切片,数组,Go,底层,赋值,陷阱,语言
来源: https://blog.csdn.net/qq_41393481/article/details/120731670