内置函数

Built-in functions 内置函数

原文:https://go.dev/ref/spec#Built-in_functions

​ 内置函数是预先声明的。它们像其他函数一样被调用,但其中一些函数接受一个类型而非表达式作为其第一个实参。

​ 内置函数没有标准的Go类型,所以它们只能出现在调用表达式中;它们不能作为函数值使用。

Close

​ 对于一个核心类型通道的参数ch,内置函数close记录了通道上将不再有任何值被发送。如果ch是一个仅接收的通道,那么(关闭它)是一个错误。发送到或关闭一个已关闭的通道会导致运行时恐慌。关闭nil通道也会引起运行时恐慌。在调用close后,并且在任何先前发送的值被接收后,接收操作将返回通道类型的零值而不阻塞。多值接收操作会返回一个接收值以及通道是否被关闭的指示。

Length and capacity 长度和容量

​ 内置函数lencap接受各种类型的实参并返回int类型的结果。该实现保证结果总是适合于一个int

Call      Argument type    Result
调用        实参类型          结果

len(s)    string type      string length in bytes
          [n]T, *[n]T      array length (== n)
          []T              slice length
          map[K]T          map length (number of defined keys)
          chan T           number of elements queued in channel buffer
          type parameter   see below

cap(s)    [n]T, *[n]T      array length (== n)
          []T              slice capacity
          chan T           channel buffer capacity
          type parameter   see below

​ 如果参数类型是一个类型参数P,调用len(e)(或cap(e))必须对P的类型集中的每个类型有效。其结果是(类型对应P实例化时使用的类型实参的)实参的长度(或容量)。

​ 切片的容量是底层数组中分配到的元素的数量。在任何时候,以下关系都是成立的:

0 <= len(s) <= cap(s)

nil切片、nil映射或nil通道的长度是0nil切片或nil通道的容量是0。

​ 如果s是一个字符串常量,那么表达式len(s)就是常量。如果s的类型是一个数组或指向数组的指针,并且表达式s不包含通道接收或(非常量)函数调用,那么表达式len(s)cap(s)是常量;在这种情况下,s不被求值。否则,lencap的调用不是常量,s被求值。

1
2
3
4
5
6
7
8
const (
	c1 = imag(2i)                    // imag(2i) = 2.0 is a constant
	c2 = len([10]float64{2})         // [10]float64{2} contains no function calls
	c3 = len([10]float64{c1})        // [10]float64{c1} contains no function calls
	c4 = len([10]float64{imag(2i)})  // imag(2i) is a constant and no function call is issued
	c5 = len([10]float64{imag(z)})   // invalid: imag(z) is a (non-constant) function call
)
var z complex128

Allocation 分配

​ 内置函数new接收一个类型T,在运行时为该类型的变量分配存储空间,并返回一个指向它的*T类型的值。该变量被初始化,如初始值一节中所述。

1
new(T)

举例来说

1
2
type S struct { a int; b float64 }
new(S)

为一个S类型的变量分配存储空间,初始化它(a=0b=0.0),并返回一个包含该位置地址的*S类型的值。

Making slices, maps and channels 制作切片、映射和通道

​ 内置函数make接收一个类型T,后面可以选择一个特定类型的表达式列表。T核心类型必须是一个切片、映射或通道。它返回一个类型为T(不是*T)的值。内存被初始化,如初始值一节中所述。

Call             Core type    Result

make(T, n)       slice        slice of type T with length n and capacity n
make(T, n, m)    slice        slice of type T with length n and capacity m

make(T)          map          map of type T
make(T, n)       map          map of type T with initial space for approximately n elements

make(T)          channel      unbuffered channel of type T
make(T, n)       channel      buffered channel of type T, buffer size n

​ 每个大小实参nm必须是整型,或者是一个只包含整型的类型集,或者是一个无类型的常量。一个常量大小参数必须是非负数,并且可以用int类型的值表示;如果它是一个无类型的常量,它被赋予int类型。如果nm都被提供并且是常量,那么n必须不大于m。对于切片和通道,如果n在运行时是负数或者大于m,就会发生运行时恐慌

1
2
3
4
5
6
s := make([]int, 10, 100)       // slice with len(s) == 10, cap(s) == 100
s := make([]int, 1e3)           // slice with len(s) == cap(s) == 1000
s := make([]int, 1<<63)         // illegal: len(s) is not representable by a value of type int => 非法的: len(s) 不能被 int 类型的值表示
s := make([]int, 10, 0)         // illegal: len(s) > cap(s) => 非法的: len(s) > cap(s)
c := make(chan int, 10)         // channel with a buffer size of 10
m := make(map[string]int, 100)  // map with initial space for approximately 100 elements

​ 调用map类型和大小提示nmake将创建一个初始空间可容纳n个map元素的map具体的行为是依赖于实现的

Appending to and copying slices 追加和复制切片

​ 内置函数appendcopy可以帮助进行常见的切片操作。对于这两个函数,其结果与实参所引用的内存是否重叠无关。

The variadic function append appends zero or more values x to a slice s and returns the resulting slice of the same type as s. The core type of s must be a slice of type []E. The values x are passed to a parameter of type ...E and the respective parameter passing rules apply. As a special case, if the core type of s is []byte, append also accepts a second argument with core type bytestring followed by .... This form appends the bytes of the byte slice or string.

可变参数函数append零个或多个值x追加到一个切片s,并返回与s相同类型的结果切片。值x被传递给一个类型为...E的参数,各自的参数传递规则适用。作为一个特例,如果s核心类型[]byteappend也接受第二个参数,其核心类型是bytestring,后面是... 。这种形式追加了字节切片或字符串的字节。

1
append(s S, x ...E) S  // core type of S is []E

​ 如果s的容量不足以容纳额外的值,append会分配一个新的、足够大的底层数组,同时容纳现有的切片元素和额外的值。否则,append复用原来的底层数组。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
s0 := []int{0, 0}
s1 := append(s0, 2)                // append a single element     s1 == []int{0, 0, 2}
s2 := append(s1, 3, 5, 7)          // append multiple elements    s2 == []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...)            // append a slice              s3 == []int{0, 0, 2, 3, 5, 7, 0, 0}
s4 := append(s3[3:6], s3[2:]...)   // append overlapping slice    s4 == []int{3, 5, 7, 2, 3, 5, 7, 0, 0}

var t []interface{}
t = append(t, 42, 3.1415, "foo")   //                             t == []interface{}{42, 3.1415, "foo"}

var b []byte
b = append(b, "bar"...)            // append string contents      b == []byte{'b', 'a', 'r' }

​ 函数copy将切片元素从源src复制到目标dst并返回复制的元素数量。两个参数的核心类型必须是具有一致的元素类型的切片。复制的元素数是len(src)len(dst)中的最小值。作为一种特殊情况,如果目标的核心类型[]bytecopy也接受一个核心类型为bytestring的源参数。这种形式将字节切片或字符串中的字节复制到字节切片中。

1
2
copy(dst, src []T) int
copy(dst []byte, src string) int

例子:

1
2
3
4
5
6
var a = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
var s = make([]int, 6)
var b = make([]byte, 5)
n1 := copy(s, a[0:])            // n1 == 6, s == []int{0, 1, 2, 3, 4, 5}
n2 := copy(s, s[2:])            // n2 == 4, s == []int{2, 3, 4, 5, 4, 5}
n3 := copy(b, "Hello, World!")  // n3 == 5, b == []byte("Hello")

Deletion of map elements 删除映射元素

​ 内置函数delete可以从映射m中删除键值k的元素,值k必须可以分配m的键类型。

1
delete(m, k)  // remove element m[k] from map m

​ 如果m的类型是类型参数,那么该类型集合中的所有类型必须是映射,并且它们必须都有相同的键类型

​ 如果映射mnil或者元素m[k]不存在,delete就是一个空操作。

Manipulating complex numbers 操纵复数

​ 有三个函数组装分解复数。内置函数 complex 从浮点实部和虚部构造一个复数值,而 realimag 则提取复数值的实部和虚部。

1
2
3
complex(realPart, imaginaryPart floatT) complexT
real(complexT) floatT
imag(complexT) floatT

​ 实参的类型和返回值相对应。对于complex函数,两个实参必须是相同的浮点类型,返回类型是具有对应浮点成分的复数类型float32类型的实参对应comple64复数类型,而float64类型的实参对应comple128复数类型。如果其中一个实参的值是一个无类型常量,那么它首先会被隐式转换为另一个实参的类型。如果两个参数都求值为无类型常量,那么它们必须是非复数,或者它们的虚数部分必须为零,这样函数的返回值就是一个无类型的复数常量。

​ 对于realimag,实参必须是复数类型,返回类型是相应的浮点类型:float32对应complex64float64对应complex128。如果实参的值是一个无类型的常量,它必须是一个数字,这样函数的返回值就是一个无类型的浮点常量。

realimag函数一起构成了复数的逆运算,所以对于一个复数类型Z的值z来说,z == Z(complex(real(z), imag(z)))

​ 如果这些函数的操作数都是常量,返回值就是一个常量。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var a = complex(2, -2)             // complex128 <=仍有疑问??怎么推导出来是 complex128 ?
const b = complex(1.0, -1.4)       // untyped complex constant 1 - 1.4i
x := float32(math.Cos(math.Pi/2))  // float32
var c64 = complex(5, -x)           // complex64
var s int = complex(1, 0)          // untyped complex constant 1 + 0i can be converted to int
_ = complex(1, 2<<s)               // illegal: 2 assumes floating-point type, cannot shift => 非法的:2 被认为是 浮点类型,不能移位 <=仍有疑问??2为什么是浮点类型?怎么推导出来的
var rl = real(c64)                 // float32
var im = imag(a)                   // float64
const c = imag(b)                  // untyped constant -1.4
_ = imag(3 << s)                   // illegal: 3 assumes complex type, cannot shift => 非法的:3 被认为是复数类型,不能移位 <=仍有疑问??3为什么是浮点类型?怎么推导出来的

Arguments of type parameter type are not permitted.

不允许使用参数类型的实参。

Handling panics 处理恐慌

​ 两个内置函数,panicrecover,协助报告和处理运行时恐慌和程序定义的错误情况。

1
2
func panic(interface{})
func recover() interface{}

​ 在执行函数F时,对panic的显式调用或运行时恐慌终止了F的执行,然后被F延迟的任何函数会照常执行。接下来,任何被F的调用者延迟的函数都会被运行,以此类推,直到被执行中的goroutine中的顶级函数所延迟的任何函数。此时,程序被终止,错误情况被报告,包括panic的实参值。这个终止过程被称为panicking

1
2
3
panic(42)
panic("unreachable")
panic(Error("cannot parse"))

recover函数允许程序管理 panicking goroutine 的行为。假设函数G延迟了一个调用recover的函数D,并且在G执行的同一个goroutine上的一个函数发生了恐慌。当被延迟函数的运行到达D时,D调用recover的返回值将是传递给调用panic的值。如果D正常返回,没有启动新的panic,则 panicking goroutine 停止。在这种情况下,G和对panic的调用之间调用的函数的状态被丢弃,并恢复正常执行。GD之前被延迟的任何函数随后被运行,G的执行通过返回给它的调用者而终止。

​ 如果以下任何一个条件成立,recover的返回值为nil

  • panic的参数是空的。
  • goroutine没有陷入panicking。
  • recover不是由被延迟函数直接调用的。

下面的例子中的protect函数调用了函数实参g,并保护调用者免受g引发的运行时恐慌。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
func protect(g func()) {
	defer func() {
		log.Println("done")  // Println executes normally even if there is a panic
		if x := recover(); x != nil {
			log.Printf("run time panic: %v", x)
		}
	}()
	log.Println("start")
	g()
}

Bootstrapping 引导

​ 目前的实现提供了几个在引导(bootstrapping)过程中有用的内置函数。为了完整起见,这些函数被记录下来,但不保证会留在语言中。它们并不返回结果。

1
2
3
4
function   Behavior

print      prints all arguments; formatting of arguments is implementation-specific => 打印所有实参;实参的格式化和实现有关
println    like print but prints spaces between arguments and a newline at the end => 和 print 类似,但是会在每个实参间打印空格,在结尾打印新行

实现限制:printprintln不一定需要接受任意的实参类型,但必须支持布尔型、数字型和字符串型的打印。

最后修改 June 5, 2023: 更新标准库 (33f199b)