Golang 语法相关笔记

Posted on 2023年7月21日周五 技术

Basic Types

推荐用 new 来创建指针, int 类型还有对应的 untype 类型,作为对应的编译器常量,精度在 256 bits 以上。

Numbers

只有 intergers 有跟随编译器变化位数的 int 类型,其他都指定了位数,例如只有 float64 类型没有 float 类型。

intergers

一般和外界对接要指明符号和大小,但程序内更推荐使用int。

floating-point numbers

complex numbers

Strings

string 都是 immutable 的,所以直接拼接 string 很浪费内存,类似 java 中的 StringBuilder,Go 中有 bytes.Buffer 来临时存放字符,最后再转化成 string。

所有 string 采用 UTF-8 编码,因此 string 的 len 不是其字符数量(UTF-8 编码有可能超过一个 byte),可以用 utf8.RuneCountInString 来获得其中 rune 字符的长度。

rune 代表一个 UTF-8 字符,格式为:

UTF-8 有如下特性:

Booleans

Aggregate Types

Arrays

array 可以在初始化的时候指定每一位的元素值,如下面例子

type Currency int

const (
		USE Currency = iota
		EUR
		GBP
		RMB
)

symbol := […]string{USD: “$”, EUR: “€”, GBP:”£”, RMB: “¥”}

fmt.Println(RMB, symbol[RMB]) // “3 ¥”

Structs

Reference Types

Pointers

For a of pointer to array type: a[x] is shorthand for (*a)[x]

If a is a pointer to an array, a[low : high] is shorthand for (*a)[low : high].

If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m()

statement *p++ is the same as (*p)++.

Slices

slice 之间不能用 == 比较符号。

唯一被允许的比较是 slice == nil,为 slice 内置 == 比较符号会有一些deep comparison的问题:

  1. array 不一样, slice 的底层数据结构是 indirect 的,甚至可能出现一个 slice 中的元素引用这个 slice 的情况。
  2. slice 的底层 array 是有可能被改变的,移除内置 ==slice 无法作为 mapkey

Maps

Spec

map 中的 value 无法被取地址,这是为了防止 rehash 导致地址变化带来问题。因此你无法对 map 中的 value 唤起 pointer method,因为没有地址可传入。

mapslice 一样,没有内置的 == 操作

数据结构

type hmap struct { ... } ,其中 B uint8 代表当前哈希表持有的 buckets 数量的 2 次幂,即 len(buckets) = 2B2^B

每个 bucket 对应的是 runtime.bmap,一个 bucket 会存 8 个键值对,一旦桶溢出,多余的键值对则会转移到溢出桶。

寻址

用 key hash 的后面几位决定目标桶,用 key hash 的前 8 位比较 key 是否相等。

for loop 时 runtime 会 random 出一个起点,如果中途修改 map 中的 kv,被修改的 kv 能否被遍历到取决于修改时 iterator 的位置是否在被修改 kv 的前面。

扩容

扩容不会修改 hash 因子,在扩容期间向旧桶写入、删除数据时会引发旧桶数据向新桶分流。

Functions

注意 receiver 定义为指针类型的情况,调用时就自动转化 value 为指针的默认行为是为了方便指针方法修改 receiver 内部状态,如果 receiver 是 value 其内部状态就不会被修改。这一点和 C 的 const 有点类似。

Channels

从已经关闭的 channel 接收消息会立即得到零值,可以用 _, ok := <-ch 的方法判断 channel 是否已经关闭。

close 已关闭的 channel 或者 nil channel 会导致 panic。

nil channel 接收消息会 block forever。

channel 的计算效率目前和直接使用锁相比并不高。

Interfaces

一个 interface 里包含了两个值,代表其指向元素的 typevalue ,因此 val i interface{} := new(SomeType) 中的 i 并不等于 nil ,因为它还包含了一个 type 值。

Basic interfaces

Embedded interfaces

General interfaces

类型比较

  1. A named type is always different from any other type;
  2. Otherwise, two types are identical if their underlying type literals are structurally equivalent.

whether two values may be compared with == and != is related to assign ability to: in any comparison, the first operand must be assignable to the type of the second operand or vice versa.

Arithmetic operators apply to numeric values and yield a result of the same type as the first operand.

Range

range 右边的表达式叫做 range expression

range expression 的 core type 只能是:

range 关键词本质上是一个语法糖,其在编译后会被展开为一个 for,针对不同结构体,展开结果不同,大致形式为:

ha := a
hv1 := 0
hn := len(ha)
v1 := hv1
v2 := nil
for ; hv1 < hn; hv1++ {
		tmp := ha[hv1]
		v1, v2 = hv1, tmp
		...
}
Core type

non-interface 的 core type 和其 underlying type 一样

interface在这两种情况下有core type

  1. 其 type set 中的所有 type 有相同的一个 underlying type U,那么其 core type 为 U
  2. type set 只有 channel type,方向相同,返回的都是 type E,那么其 core type 为 E

创建变量

没有被给定指定值的变量都会被赋予zero value。

定义变量只是定义变量之间的DAG,真正的初始化从第一个没有依赖其他变量的变量开始。https://go.dev/ref/spec#Package_initialization

占位符

%d 十进制整数

%x %o %b 十六进制、八进制、二进制整数

%f %g %e 浮点数,e 表示 3.1415923e+00 的形式,g 指根据情况选择 f 或 e 输出(看哪个输出格式更紧凑)。All three verbs allow field width and numeric precision to be controlled.

%t 布尔值

%c rune、unicode code point

%s 字符串、凡是实现了Stringer interface的类型都适用

%q quoted string “abc” or rune ‘c'

%v any value in a natural format

%T type of any value

%% literal percent sign (no operand)

%w error values

%[1]d 代表把 format 的第一个参数用十进制整数的形式打印出来,这样多个占位符可以用上相同的参数。举个例子:

	fmt.Sprintf("%[2]d %[1]d\n", 11, 22) // "22 11"

%#d 代表把前面表示进制(0、0x)的前缀给打印出来

% x,中间有个空格,代表把数字中的每个元素按照 %x 的格式打印出来,每个元素之间用空格分隔。

JSON

JSON marshal是依赖反射读取field tag以实现序列化

https://github.com/bytedance/mockey 中的 mock 是如何实现的

通过替换函数对应地址的字节码,将函数调用引导进 mock 函数。