源码阅读苦练基本功Golang内置函数分析

Posted 在路上的德尔菲

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了源码阅读苦练基本功Golang内置函数分析相关的知识,希望对你有一定的参考价值。

Golang package builtin 中内置函数我分为两部分介绍,第一部分为基础类型,第二部分为包含函数、接口,较复杂一些

第一部分

// bool is the set of boolean values, true and false.
type bool bool

// true and false are the two untyped boolean values.
const (
	true  = 0 == 0 // Untyped bool.
	false = 0 != 0 // Untyped bool.
)

// uint8 is the set of all unsigned 8-bit integers.
// Range: 0 through 255.
type uint8 uint8

// uint16 is the set of all unsigned 16-bit integers.
// Range: 0 through 65535.
type uint16 uint16

// uint32 is the set of all unsigned 32-bit integers.
// Range: 0 through 4294967295.
type uint32 uint32

// uint64 is the set of all unsigned 64-bit integers.
// Range: 0 through 18446744073709551615.
type uint64 uint64

// int8 is the set of all signed 8-bit integers.
// Range: -128 through 127.
type int8 int8

// int16 is the set of all signed 16-bit integers.
// Range: -32768 through 32767.
type int16 int16

// int32 is the set of all signed 32-bit integers.
// Range: -2147483648 through 2147483647.
type int32 int32

// int64 is the set of all signed 64-bit integers.
// Range: -9223372036854775808 through 9223372036854775807.
type int64 int64

// float32 is the set of all IEEE-754 32-bit floating-point numbers.
type float32 float32

// float64 is the set of all IEEE-754 64-bit floating-point numbers.
type float64 float64

// complex64 is the set of all complex numbers with float32 real and
// imaginary parts.
type complex64 complex64

// complex128 is the set of all complex numbers with float64 real and
// imaginary parts.
type complex128 complex128

// string is the set of all strings of 8-bit bytes, conventionally but not
// necessarily representing UTF-8-encoded text. A string may be empty, but
// not nil. Values of string type are immutable.
type string string

// int is a signed integer type that is at least 32 bits in size. It is a
// distinct type, however, and not an alias for, say, int32.
type int int

// uint is an unsigned integer type that is at least 32 bits in size. It is a
// distinct type, however, and not an alias for, say, uint32.
type uint uint

// uintptr is an integer type that is large enough to hold the bit pattern of
// any pointer.
type uintptr uintptr

// byte is an alias for uint8 and is equivalent to uint8 in all ways. It is
// used, by convention, to distinguish byte values from 8-bit unsigned
// integer values.
type byte = uint8

// rune is an alias for int32 and is equivalent to int32 in all ways. It is
// used, by convention, to distinguish character values from integer values.
type rune = int32

主要注意几个点:

  • type int int 大小至少32位也就是至少4字节,为什么说是至少呢?因为int类型所占字节数跟操作系统有关,如果是32位就是4个字节,如果是64位就是8个字节,type int inttype int32 int32不同,int32永远是32位,int64永远是64位。
  • type string string string可能是空,但不能为nil,这一点和Java不同,Java中String可以为null。string是不可变的,这一点和Java相同。
  • type uintptr uintptr 是一个integer类型,空间足够大可存储任何指针。
  • byteuint8类型是相同的,可以说是uint8的别称,byte更简单一些。
  • runeint32类型是相同的,rune更简单一些。

第二部分

any

// any is an alias for interface and is equivalent to interface in all ways.
type any = interface

anyinterface 是相同的,不要以为是泛型关键词,为了少写一些字母所以会用any,下面举了一个例子

	func convert(t any)
		switch t.(type)
			case int:
				//
			case string:
				//
			case bool:
				//
		
	
	
	func main() 
		f(2)
		f(true)
		f("煎鱼好!")
	

comparable

// comparable is an interface that is implemented by all comparable types
// (booleans, numbers, strings, pointers, channels, arrays of comparable types,
// structs whose fields are all comparable types).
// The comparable interface may only be used as a type parameter constraint,
// not as the type of a variable.
type comparable interface comparable 

comparable是由所有可比较类型(包括booleans, numbers, strings, pointers, channels,interface, arrays中元素为可比较类型,
以及structs 中所有属性都是可比较类型)实现的接口。只能用于类型参数约束,不用于变量类型比较,举个🌰:

	func SumNumbers[K comparable, V int](m map[K]V) V 
	    var s V
	    for _, v := range m 
	        s += v
	    
	    return s
	

	//入参
    ints := map[string]int
        "first":  34,
        "second": 12,
    
    //调用
	SumIntsOrFloats[string, int](ints)

  1. T is not an interface type and T supports the operations == and !=; or
  2. T is an interface type and each type in T’s type set implements comparable.

下面大多数都是上面1类型,其中interface属于上面2类型。支持的类型以下列举出来:

  • Boolean values are comparable. Two boolean values are equal if they are either both true or both false.
  • Integer values are comparable and ordered, in the usual way.
  • Floating-point values are comparable and ordered, as defined by the IEEE-754 standard.
  • Complex values are comparable. Two complex values u and v are equal if both real(u) == real(v) and imag(u) == imag(v).
  • String values are comparable and ordered, lexically byte-wise.
  • Pointer values are comparable. Two pointer values are equal if they point to the same variable or if both have value nil. Pointers to distinct zero-size variables may or may not be equal.
  • Channel values are comparable. Two channel values are equal if they were created by the same call to make or if both have value nil.
  • Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.for example interface ~int | ~string
  • A value x of non-interface type X and a value t of interface type T are comparable when values of type X are comparable and X implements T. They are equal if t’s dynamic type is identical to X and t’s dynamic value is equal to x.
  • Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.
  • Array values are comparable if values of the array element type are comparable. Two array values are equal if their corresponding elements are equal.

Java中Comparable<T>接口是用于比较排序的,Golang中comparable是用于类型限定,功能不同,需要注意。


public interface Comparable<T> 
    public int compareTo(T o);



iota

// iota is a predeclared identifier representing the untyped integer ordinal
// number of the current const specification in a (usually parenthesized)
// const declaration. It is zero-indexed.
const iota = 0 // Untyped int.
  • iota在关键字const出现时重置为0
  • const中每新增一行常量声明将使iota计数一次,下面举了三个例子
const (
		n1 = iota //0
		n2        //1
		n3        //2
		n4        //3
	)
const (
		n1 = iota //0
		n2        //1
		_         //有误
		n4        //2
	)	
const (
		a,b = iota,iota + 1 //0,1
		c,d                 //1,2
		e,f                 //2,3
	)

nil

// nil is a predeclared identifier representing the zero value for a
// pointer, channel, func, interface, map, or slice type.
var nil Type 
  • nil 只能为 pointer, channel, func, interface, map, or slice 类型,不能为基本数据类型

Type

// Type/Type1 is here for the purposes of documentation only. It is a stand-in
// for any Go type, but represents the same type for any given function
// invocation.
type Type int
type Type1 int

// IntegerType is here for the purposes of documentation only. It is a stand-in
// for any integer type: int, uint, int8 etc.
type IntegerType int

// FloatType is here for the purposes of documentation only. It is a stand-in
// for either float type: float32 or float64.
type FloatType float32

// ComplexType is here for the purposes of documentation only. It is a
// stand-in for either complex type: complex64 or complex128.
type ComplexType complex64

Go 为预定义标识提供的文档说明,在IDE中使用,并非真正实现,不用特别关注。


append


// The append built-in function appends elements to the end of a slice. If
// it has sufficient capacity, the destination is resliced to accommodate the
// new elements. If it does not, a new underlying array will be allocated.
// Append returns the updated slice. It is therefore necessary to store the
// result of append, often in the variable holding the slice itself:
//	slice = append(slice, elem1, elem2)
//	slice = append(slice, anotherSlice...)
// As a special case, it is legal to append a string to a byte slice, like this:
//	slice = append([]byte("hello "), "world"...)
func append(slice []Type, elems ...Type) []Type
  • append 函数只能用于slice数据结构上,将元素添加到切片末尾并返回结果
  • 调用append函数只能用原来的切片变量接收返回值
  • append追加元素,如果底层数组有空间的话,将新元素放在切片剩余空间中,如果底层数据没有空间,会新创建一个新的底层数组,将原来切片中元素拷贝到新切片中,返回一个新的切片,切片地址信息也会发生改变。
  • append扩容原理,在一定大小内容量内cap会double增加,在大于阈值后cap按1.25倍增加,目的是节约提前分配空间。
/*例子1*/
	var boys []string
	//len(boys) = 4, cap(boys) = 4
	boys = append(boys, "panda", "jack", "bingo", "hans")
	//len(boys) = 5, cap(boys) = 8
	boys = append(boys, "baobao")

/*例子2*/
	girls := []string"happy","doudou","marni"
	girls2 := []string"jiu"
	girls = append(girls, girl2...)


/*例子3*/
	intSlice := []int1,2,3,4,5,6,7,8,9
	//4,5,6,7,8,9
	intSlice2 := append(intSlice[:0],intSlice[3:]...)
	//1,2,3,7,8,9
	intSlice3 := append(intSlice[:3],intSlice[:6]...)
	//1,2,3,4,5,6
	intSlice4 := append(intSlice[:0],intSlice[:6]...)

/*例子4反面例子*/	
	slice1 : = []string"hello"
	name := "world"
	slice2 := []string"goodbye"
	//编译错误,必须有接收值
	append(slice1, name)
	//编译错误,接收slice2不是原slice1
	slice2 = append(slice1, name)

copy

// The copy built-in function copies elements from a source slice into a
// destination slice. (As a special case, it also will copy bytes from a
// string to a slice of bytes.) The source and destination may overlap. Copy
// returns the number of elements copied, which will be the minimum of
// len(src) and len(dst).
func copy(dst, src []Type) int
  • copy从一个切片拷贝到另一个目的切片上,原切片和目的切片可能交叉,
  • copy返回复制的元素的个数,为len(src)len(dst)中的最小值
  • 注意copy函数第一个参数是目的切片,第二个参数是原切片
/*例子*/
	slice1 := []int1,2,3,4,5
	slice2 := []int6,7,8
	//slice1 6,7,8,4,5 ,返回3
	copy(slice1, slice2)
	//slice2 1,2,3 ,返回3
	copy(slice2, slice1)
	//slice2 2,3,4 ,返回3
	copy(slice2, slice1[1:4])

有点类似Java中Arrays.copy()


delete

// The delete built-in function deletes the element with the specified key
// (m[key]) from the map. If m is nil or there is no such element, delete
// is a no-op.
func delete(m map[Type]Type1, key Type)
  • delete函数用于删除map的key-value,类似于Java Map.remove(Key)操作
  • delete函数第一个参数为map,第二个参数为key值,注意如果map为空或者map中没有该key,相当于没有做任何操作
  • 没有返回值,并不知道是否有删除操作或者没有删除操作,需要通过map的大小来判断吗?

len / cap

// The len built-in function returns the length of v, according to its type:
//	Array: the number of elements in v.
//	Pointer to array: the number of elements in *v (even if v is nil).
//	Slice, or map: the number of elements in v; if v is nil, len(v) is zero.
//	String: the number of bytes in v.
//	Channel: the number of elements queued (unread) in the channel buffer;
//	         if v is nil, len(v) is zero.
// For some arguments, such as a string literal or a simple array expression, the
// result can be a constant. See the Go language specification's "Length and
// capacity" section for details.
func len(v Type) int

// The cap built-in function returns the capacity of v, according to its type:
//	Array: the number of elements in v (same as len(v)).
//	Pointer to array: the number of elements in *v (same as len(v)).
//	Slice: the maximum length the slice can reach when resliced;
//	if v is nil, cap(v) is zero.
//	Channel: the channel buffer capacity, in units of elements;
//	if v is nil, cap(v) is zero.
// For some arguments, such as a simple array expression, the result can be a
// constant. See the Go language specification's "Length and capacity" section for
// details.
func cap(v Type) int
  • len函数可用于array、指向Array的指针、slice、map、string、channel
  • cap函数可用于array、指向Array的指针、slice、channel,相比len少了string和map
/*例子1*/
	array := [5]int1,2,3
	//len(array)为5,cap(array)为5
	
/*例子2*/
	arrayP := &[5]int1,2,3
	//len(arrayP)为5,cap(arrayP)为5

/*例子3*/
	map1 := map[string]int"panda":4,"marni":3
	//len(map1)为2

    //slice省略在上面append函数章节有解释
/*例子4*/
	str := "hello world"
	//len(str)为11

/*例子5*/
	intChan = make(chan int, 3)
	intChan<-10
	intChan<-20
	//len(intChan)为2,cap(intChan)为3

make

// The make built-in function allocates and initializes an object of type
// slice, map, or chan (only). Like new, the first argument is a type, not a
// value. Unlike new, make's return type is the same as the type of its
// argument, not a pointer to it. The specification of the result depends on
// the type:
//	Slice: The size specifies the length. The capacity of the slice is
//	equal to its length. A second integer argument may be provided to
//	specify a different capacity; it must be no smaller than the
//	length. For example, make([]int, 0, 10) allocates an underlying array
//	of size 10 and returns a slice of length 0 and capacity 10 that is
//	backed by this underlying array.
//	Map: An empty map is allocated with enough space to hold the
//	specified number of elements. The size may be omitted, in which case
//	a small starting size is allocated.
//	Channel: The channel's buffer is initialized with the specified
//	buffer capacity. If zero, or the size is omitted, the channel is
//	unbuffered.
func make(t Type, size ...IntegerType) Type
  • make函数用于分配内存空间,初始化对象,但是只能用于slice、map、chan类型
  • make第一个参数是类型,不是值,和new函数不同的是返回的是这个类型,而不是指向这个类型的指针,下面分别讲解一下三种类型的
/*例子1*/
	//len为0,cap为10
	slice1 := make([]int, 0, 10)
	//len为10,cap为10
	slice2 := make([]int,10)
	
/*例子2*/
	//map无需指定容量大小,初始化一个空map
	map1 := make(map[string]int)
/*例子3*/
	//通道缓存设置为10
	channel1 := make(chan int, 10)

	//如果为零或忽略大小(不传入第二个参数),则 channel 为无缓冲的
	channel2 := make(chan int, 0)
	channel3 := make(chan int)

new

// The new built-in function allocates memory. The first argument is a type,
// not a value, and the value returned is a pointer to a newly
// allocated zero value of that type.
func new(Type) *Type

new函数是分配内存,输入的参数是类型不是值,返回值是这种类型零值的指针,下面举几个🌰

/*例子1*/
	num5 := new(int)
	fmt.Printf("num5的类型=%T, num5的值=%v, num5的地址=%v\\n", num5, num5, &num5)
	//num5的类型=*int, num5的值=0xc00018e0d0, num5的地址=0xc000186008

/*例子2*/
	new(pb.ArithService)
	//ArithService结构体
	type ArithService struct

/*例子3*/
	new(Call)
	//Call结构体
	type Call struct 
		ServiceMethod string     // The name of the service and method to call.
		Args          any        // The argument to the function (*struct).
		Reply         any        // The reply from the function (*struct).
		Error         error      // After completion, the error status.
		Done          chan *Call // Receives *Call when Go is complete.


complex / real / imag

// The complex built-in function constructs a complex value from two
// floating-point values. The real and imaginary parts must be of the same
// size, either float32 or float64 (or assignable to them), and the return
// value will be the corresponding complex type (complex64 for float32,
// complex128 for float64).
func complex(r, i FloatType) ComplexType

// The real built-in function returns the real part of the complex number c.
// The return value will be floating point type corresponding to the type of c.
func real(c ComplexType) FloatType

// The imag built-in function returns the imaginary part of the complex
// number c. The return value will be floating point type corresponding to
// the type of c.
func imag(c ComplexType) FloatType

复数一般不会用到,不做过多解释。


close

// The close built-in function closes a channel, which must be either
// bidirectional or send-only. It should be executed only by the sender,
// never the receiver, and has the effect of shutting down the channel after
// the last sent value is received. After the last value has been received
// from a closed channel c, any receive from c will succeed without
// blocking, returning the zero value for the channel element. The form
//	x, ok := <-c
// will also set ok to false for a closed channel.
func close(c chan<- Type)
  • close函数用于chan,chan必须为双向通道或者是单向输入的(send-only)通道,close作用是不允许再向通道中添加元素,从通道中取出元素时不受影响的。
  • 当从关闭的通道取出最后一个元素,再从通道取到的将是零值。
  • x, ok := <-c 对于close来说返回ok为false。

panic

// The panic built-in function stops normal execution of the current
// goroutine. When a function F calls panic, normal execution of F stops
// immediately. Any functions whose execution was deferred by F are run in
// the usual way, and then F returns to its caller. To the caller G, the
// invocation of F then behaves like a call to panic, terminating G's
// execution and running any deferred functions. This continues until all
// functions in the executing goroutine have stopped, in reverse order. At
// that point, the program is terminated with a non-zero exit code. This
// termination sequence is called panicking and can be controlled by the
// built-in function recover.
func panic(v any)
  • panic发生后程序会中断执行,按调用堆栈顺序层层不断向上抛,最后返回一个非0退出码,一般会返回2错误码。
  • panic是底层抽象的错误,空指针、越界等异常具体异常,一般在业务代码里不要直接使用panic
  • 使用panic的场景,举个例子当项目中特别依赖一些组件时,比如一些web项目中经常会在进程启动之前初始化一些mysql,mq句柄。这些实例对业务来说是非常重要的,所以当这些实例初始化失败时我们可以直接让当前程序panic(手动panic),然后及时发现问题并解决。
  • panic函数参数可以是任何类型,下面recover函数是用来处理控制panic的。

recover

// The recover built-in function allows a program to manage behavior of a
// panicking goroutine. Executing a call to recover inside a deferred
// function (but not any function called by it) stops the panicking sequence
// by restoring normal execution and retrieves the error value passed to the
// call of panic. If recover is called outside the deferred function it will
// not stop a panicking sequence. In this case, or when the goroutine is not
// panicking, or if the argument supplied to panic was nil, recover returns
// nil. Thus the return value from recover reports whether the goroutine is
// panicking.
func recover() any
  • recover用于处理控制paniking行为,捕捉panic,恢复程序正常执行,recover返回值为panic发生时的记录,包括panic 设置的入参和函数调用的堆栈跟踪信息。
  • 如果recover放在defer函数以外,recover返回为nil,将不起控制panic的作用,下面反例1展示。
  • 注意将defer语句写在函数的最前面,放在panic后面将不起作用。
/*正面例子*/
	defer func() 
		err := recover()
		if err != nil 
			fmt.Println("err is", err)
		
	()
	panic(errors.New("something wrong"))
	
/*演示几种recover错误用法*/
	//反例1,recover错误用法,此次没有panic发生,recover捕捉不到任何东西,返回nil
	fmt.Printf("no panic: %v\\n", recover())

	//反例2,引发panic,此时程序终止,不会走到下面p := recover()
	panic(errors.New("something wrong"))
	//不会走到此unreachale code
	p := recover()
	fmt.Printf("panic: %s\\n", p)

print / println

// The print built-in function formats its arguments in an
// implementation-specific way and writes the result to standard error.
// Print is useful for bootstrapping and debugging; it is not guaranteed
// to stay in the language.
func print(args ...Type)

// The println built-in function formats its arguments in an
// implementation-specific way and writes the result to standard error.
// Spaces are always added between arguments and a newline is appended.
// Println is useful for bootstrapping and debugging; it is not guaranteed
// to stay in the language.
func println(args ...Type)
  • print()println() 标准错误打印
  • print()println() 不能打印数组、结构体具体值会打印引用地址值

除非调试启动时和debug时打印日志,其他情况不建议在代码里使用


error

// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
type error interface 
	Error() string

  • 实现error接口的Error()方法,说明就是一个error类型,如果返回结果为nil说明没有异常。
	//自定义异常结构体
	type ErrInvalidParam struct 
	    ParamName  string
	    ParamValue string
	
	
	func (e *ErrInvalidParam) Error() string 
	    return fmt.Sprintf("invalid param: %+v, value: %+v", e.ParamName, e.ParamValue)
	

	//断言机制,对ErrInvalidParam类型处理
	e, ok := err.(*ErrInvalidParam)
	if ok && e != nil 
		//...
	

	//类型选择机制,对不同类型异常处理
	if err != nil 
		switch err.(type) 
		case *ErrInvalidParam:
			//..
			return
		default:
			//...
			return
		
	

以上是关于源码阅读苦练基本功Golang内置函数分析的主要内容,如果未能解决你的问题,请参考以下文章

启航 - cache2go源码分析

Golang中的内置函数

Golang database/sql源码分析

golang map源码浅析

Qt源码阅读 moveToThread

golang channel源码阅读