Go 語言規范學習(7)

文章目錄

    • Built-in functions
      • Appending to and copying slices
      • Clear
      • Close
      • Manipulating complex numbers
      • Deletion of map elements
      • Length and capacity
      • Making slices, maps and channels
      • Min and max
      • Allocation
      • Handling panics
      • Bootstrapping
    • Packages
      • Source file organization
      • Package clause
      • Import declarations
      • An example package
    • Program initialization and execution
      • The zero value
      • Package initialization
      • Program initialization
      • Program execution
    • Errors
    • Run-time panics
    • System considerations
      • Package `unsafe`
      • Size and alignment guarantees

Built-in functions

Built-in functions are predeclared. They are called like any other function but some of them accept a type instead of an expression as the first argument.

The built-in functions do not have standard Go types, so they can only appear in call expressions; they cannot be used as function values.

Appending to and copying slices

The built-in functions append and copy assist in common slice operations. For both functions, the result is independent of whether the memory referenced by the arguments overlaps.

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(s S, x ...E) S  // core type of S is []E

If the capacity of s is not large enough to fit the additional values, append allocates a new, sufficiently large underlying array that fits both the existing slice elements and the additional values. Otherwise, append re-uses the underlying array.

s0 := []int{0, 0}
s1 := append(s0, 2)                // append a single element     s1 is []int{0, 0, 2}
s2 := append(s1, 3, 5, 7)          // append multiple elements    s2 is []int{0, 0, 2, 3, 5, 7}
s3 := append(s2, s0...)            // append a slice              s3 is []int{0, 0, 2, 3, 5, 7, 0, 0}
s4 := append(s3[3:6], s3[2:]...)   // append overlapping slice    s4 is []int{3, 5, 7, 2, 3, 5, 7, 0, 0}var t []interface{}
t = append(t, 42, 3.1415, "foo")   //                             t is []interface{}{42, 3.1415, "foo"}var b []byte
b = append(b, "bar"...)            // append string contents      b is []byte{'b', 'a', 'r' }

The function copy copies slice elements from a source src to a destination dst and returns the number of elements copied. The core types of both arguments must be slices with identical element type. The number of elements copied is the minimum of len(src) and len(dst). As a special case, if the destination’s core type is []byte, copy also accepts a source argument with core type bytestring. This form copies the bytes from the byte slice or string into the byte slice.

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

Examples:

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 is []int{0, 1, 2, 3, 4, 5}
n2 := copy(s, s[2:])            // n2 == 4, s is []int{2, 3, 4, 5, 4, 5}
n3 := copy(b, "Hello, World!")  // n3 == 5, b is []byte("Hello")

Clear

The built-in function clear takes an argument of map, slice, or type parameter type, and deletes or zeroes out all elements [Go 1.21].

Call        Argument type     Resultclear(m)    map[K]T           deletes all entries, resulting in anempty map (len(m) == 0)clear(s)    []T               sets all elements up to the length ofs to the zero value of Tclear(t)    type parameter    see below

If the type of the argument to clear is a type parameter, all types in its type set must be maps or slices, and clear performs the operation corresponding to the actual type argument.

If the map or slice is nil, clear is a no-op.

Close

For an argument ch with a core type that is a channel, the built-in function close records that no more values will be sent on the channel. It is an error if ch is a receive-only channel. Sending to or closing a closed channel causes a run-time panic. Closing the nil channel also causes a run-time panic. After calling close, and after any previously sent values have been received, receive operations will return the zero value for the channel’s type without blocking. The multi-valued receive operation returns a received value along with an indication of whether the channel is closed.

Manipulating complex numbers

Three functions assemble and disassemble complex numbers. The built-in function complex constructs a complex value from a floating-point real and imaginary part, while real and imag extract the real and imaginary parts of a complex value.

complex(realPart, imaginaryPart floatT) complexT
real(complexT) floatT
imag(complexT) floatT

The type of the arguments and return value correspond. For complex, the two arguments must be of the same floating-point type and the return type is the complex type with the corresponding floating-point constituents: complex64 for float32 arguments, and complex128 for float64 arguments. If one of the arguments evaluates to an untyped constant, it is first implicitly converted to the type of the other argument. If both arguments evaluate to untyped constants, they must be non-complex numbers or their imaginary parts must be zero, and the return value of the function is an untyped complex constant.

For real and imag, the argument must be of complex type, and the return type is the corresponding floating-point type: float32 for a complex64 argument, and float64 for a complex128 argument. If the argument evaluates to an untyped constant, it must be a number, and the return value of the function is an untyped floating-point constant.

The real and imag functions together form the inverse of complex, so for a value z of a complex type Z, z == Z(complex(real(z), imag(z))).

If the operands of these functions are all constants, the return value is a constant.

var a = complex(2, -2)             // 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
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

Arguments of type parameter type are not permitted.

Deletion of map elements

The built-in function delete removes the element with key k from a map m. The value k must be assignable to the key type of m.

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

If the type of m is a type parameter, all types in that type set must be maps, and they must all have identical key types.

If the map m is nil or the element m[k] does not exist, delete is a no-op.

Length and capacity

The built-in functions len and cap take arguments of various types and return a result of type int. The implementation guarantees that the result always fits into an int.

Call      Argument type    Resultlen(s)    string type      string length in bytes[n]T, *[n]T      array length (== n)[]T              slice lengthmap[K]T          map length (number of defined keys)chan T           number of elements queued in channel buffertype parameter   see belowcap(s)    [n]T, *[n]T      array length (== n)[]T              slice capacitychan T           channel buffer capacitytype parameter   see below

If the argument type is a type parameter P, the call len(e) (or cap(e) respectively) must be valid for each type in P’s type set. The result is the length (or capacity, respectively) of the argument whose type corresponds to the type argument with which P was instantiated.

The capacity of a slice is the number of elements for which there is space allocated in the underlying array. At any time the following relationship holds:

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

The length of a nil slice, map or channel is 0. The capacity of a nil slice or channel is 0.

The expression len(s) is constant if s is a string constant. The expressions len(s) and cap(s) are constants if the type of s is an array or pointer to an array and the expression s does not contain channel receives or (non-constant) function calls; in this case s is not evaluated. Otherwise, invocations of len and cap are not constant and s is evaluated.

const (c1 = imag(2i)                    // imag(2i) = 2.0 is a constantc2 = len([10]float64{2})         // [10]float64{2} contains no function callsc3 = len([10]float64{c1})        // [10]float64{c1} contains no function callsc4 = len([10]float64{imag(2i)})  // imag(2i) is a constant and no function call is issuedc5 = len([10]float64{imag(z)})   // invalid: imag(z) is a (non-constant) function call
)
var z complex128

Making slices, maps and channels

The built-in function make takes a type T, optionally followed by a type-specific list of expressions. The core type of T must be a slice, map or channel. It returns a value of type T (not *T). The memory is initialized as described in the section on initial values.

Call             Core type    Resultmake(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 mmake(T)          map          map of type T
make(T, n)       map          map of type T with initial space for approximately n elementsmake(T)          channel      unbuffered channel of type T
make(T, n)       channel      buffered channel of type T, buffer size n

Each of the size arguments n and m must be of integer type, have a type set containing only integer types, or be an untyped constant. A constant size argument must be non-negative and representable by a value of type int; if it is an untyped constant it is given type int. If both n and m are provided and are constant, then n must be no larger than m. For slices and channels, if n is negative or larger than m at run time, a run-time panic occurs.

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
s := make([]int, 10, 0)         // illegal: 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

Calling make with a map type and size hint n will create a map with initial space to hold n map elements. The precise behavior is implementation-dependent.

Min and max

The built-in functions min and max compute the smallest—or largest, respectively—value of a fixed number of arguments of ordered types. There must be at least one argument [Go 1.21].

The same type rules as for operators apply: for ordered arguments x and y, min(x, y) is valid if x + y is valid, and the type of min(x, y) is the type of x + y (and similarly for max). If all arguments are constant, the result is constant.

var x, y int
m := min(x)                 // m == x
m := min(x, y)              // m is the smaller of x and y
m := max(x, y, 10)          // m is the larger of x and y but at least 10
c := max(1, 2.0, 10)        // c == 10.0 (floating-point kind)
f := max(0, float32(x))     // type of f is float32
var s []string
_ = min(s...)               // invalid: slice arguments are not permitted
t := max("", "foo", "bar")  // t == "foo" (string kind)

For numeric arguments, assuming all NaNs are equal, min and max are commutative and associative:

min(x, y)    == min(y, x)
min(x, y, z) == min(min(x, y), z) == min(x, min(y, z))

For floating-point arguments negative zero, NaN, and infinity the following rules apply:

   x        y    min(x, y)    max(x, y)-0.0    0.0         -0.0          0.0    // negative zero is smaller than (non-negative) zero-Inf      y         -Inf            y    // negative infinity is smaller than any other number+Inf      y            y         +Inf    // positive infinity is larger than any other numberNaN      y          NaN          NaN    // if any argument is a NaN, the result is a NaN

For string arguments the result for min is the first argument with the smallest (or for max, largest) value, compared lexically byte-wise:

min(x, y)    == if x <= y then x else y
min(x, y, z) == min(min(x, y), z)

Allocation

The built-in function new takes a type T, allocates storage for a variable of that type at run time, and returns a value of type *T pointing to it. The variable is initialized as described in the section on initial values.

new(T)

For instance

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

allocates storage for a variable of type S, initializes it (a=0, b=0.0), and returns a value of type *S containing the address of the location.

Handling panics

Two built-in functions, panic and recover, assist in reporting and handling run-time panics and program-defined error conditions.

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

While executing a function F, an explicit call to panic or a run-time panic terminates the execution of F. Any functions deferred by F are then executed as usual. Next, any deferred functions run by F’s caller are run, and so on up to any deferred by the top-level function in the executing goroutine. At that point, the program is terminated and the error condition is reported, including the value of the argument to panic. This termination sequence is called panicking.


  1. 觸發 panic 的情況
  • 顯式調用:在函數 F 中直接調用 panic(value)
  • 運行時 panic:例如數組越界、空指針解引用等運行時錯誤。

  1. 函數 F 的執行立即停止
  • panic 發生時,函數 F 的當前代碼會立刻終止執行,后續代碼不再運行。

  1. 執行 F 中的 deferred 函數
  • Go 會按 后進先出(LIFO) 的順序執行 F 中所有通過 defer 注冊的函數。
  • 這些 defer 函數通常用于釋放資源(如關閉文件、解鎖互斥鎖等),即使發生 panic 也會確保執行。

  1. 向上傳遞 panic 到調用棧
  • 如果 F 的調用者(比如函數 G)也有 defer 函數,這些 defer 函數會接著執行。
  • 這一過程會遞歸向上傳遞,直到當前 goroutine 的最頂層函數(如 main 函數)。

  1. 程序終止并報錯
  • 當所有 defer 函數執行完畢后,程序會崩潰退出
  • 錯誤信息會包含 panic 的參數值(例如 panic("file not found") 中的字符串)。

  1. 關鍵術語:Panicking(恐慌過程)
  • 整個從 panic 觸發到程序終止的流程被稱為 panicking

panic(42)
panic("unreachable")
panic(Error("cannot parse"))

The recover function allows a program to manage behavior of a panicking goroutine. Suppose a function G defers a function D that calls recover and a panic occurs in a function on the same goroutine in which G is executing. When the running of deferred functions reaches D, the return value of D’s call to recover will be the value passed to the call of panic. If D returns normally, without starting a new panic, the panicking sequence stops. In that case, the state of functions called between G and the call to panic is discarded, and normal execution resumes. Any functions deferred by G before D are then run and G’s execution terminates by returning to its caller.


  1. 核心場景描述
  • 函數 G 中通過 defer 注冊了一個函數 D
  • D 內部調用了 recover
  • 同一 goroutine 中發生了 panic(例如在 G 或其調用的函數中觸發了 panic)。

  1. recover 的觸發時機
  • panic 發生后,Go 會按順序執行當前 goroutine 的 defer 函數。
  • 當執行到 D 時,D 中的 recover()捕獲 panic 的值,并返回該值(即 panic(value) 中的 value)。

  1. recover 如何停止 panic 的傳播
  • 如果 D 正常執行完畢(沒有觸發新的 panic):
    1. panic 的連鎖反應會立即停止,程序不再繼續向上崩潰。
    2. 丟棄 panic 后的堆棧狀態:在 G 和發生 panic 的函數之間的所有函數調用狀態會被丟棄(這些函數的剩余代碼不會執行)。
    3. 程序恢復正常執行:從 G 中尚未執行的 defer 開始繼續運行。

  1. 后續流程
  • 執行 G 中剩余的 defer:在 D 之前注冊的其他 defer 函數會按順序執行。
  • G 正常返回G 會像沒有發生過 panic 一樣返回給它的調用者。

  1. 關鍵點總結
  • recover 必須通過 defer 調用,且僅在 panic 后的 defer 函數中生效。
  • recover終止 panic 的傳播,但不會恢復 panic 發生點之后的代碼(那些代碼已被跳過)。
  • 如果 recover 后程序繼續運行,資源清理依賴 defer(例如釋放鎖、關閉文件等)。

例子:

func main() {fmt.Println("Start")G() // 調用函數 Gfmt.Println("End") // 會正常執行,因為 panic 被 recover 捕獲
}func G() {defer fmt.Println("Defer in G") // (3)defer D()                      // (2) 先注冊的 D 會后執行panic("something went wrong")  // (1) 觸發 panic
}func D() {if r := recover(); r != nil {fmt.Println("Recovered:", r) // 捕獲 panic 的值}
}

輸出

Start
Recovered: something went wrong
Defer in G
End

The return value of recover is nil when the goroutine is not panicking or recover was not called directly by a deferred function. Conversely, if a goroutine is panicking and recover was called directly by a deferred function, the return value of recover is guaranteed not to be nil. To ensure this, calling panic with a nil interface value (or an untyped nil) causes a run-time panic.

The protect function in the example below invokes the function argument g and protects callers from run-time panics raised by g.

func protect(g func()) {defer func() {log.Println("done")  // Println executes normally even if there is a panicif x := recover(); x != nil {log.Printf("run time panic: %v", x)}}()log.Println("start")g()
}

Bootstrapping

Current implementations provide several built-in functions useful during bootstrapping. These functions are documented for completeness but are not guaranteed to stay in the language. They do not return a result.

Function   Behaviorprint      prints all arguments; formatting of arguments is implementation-specific
println    like print but prints spaces between arguments and a newline at the end

Implementation restriction: print and println need not accept arbitrary argument types, but printing of boolean, numeric, and string types must be supported.

Packages

Go programs are constructed by linking together packages. A package in turn is constructed from one or more source files that together declare constants, types, variables and functions belonging to the package and which are accessible in all files of the same package. Those elements may be exported and used in another package.

Source file organization

Each source file consists of a package clause defining the package to which it belongs, followed by a possibly empty set of import declarations that declare packages whose contents it wishes to use, followed by a possibly empty set of declarations of functions, types, variables, and constants.

SourceFile = PackageClause ";" { ImportDecl ";" } { TopLevelDecl ";" } .

Package clause

A package clause begins each source file and defines the package to which the file belongs.

PackageClause = "package" PackageName .
PackageName   = identifier .

The PackageName must not be the blank identifier.

package math

A set of files sharing the same PackageName form the implementation of a package. An implementation may require that all source files for a package inhabit the same directory.

Import declarations

An import declaration states that the source file containing the declaration depends on functionality of the imported package (§Program initialization and execution) and enables access to exported identifiers of that package. The import names an identifier (PackageName) to be used for access and an ImportPath that specifies the package to be imported.

ImportDecl = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
ImportSpec = [ "." | PackageName ] ImportPath .
ImportPath = string_lit .

The PackageName is used in qualified identifiers to access exported identifiers of the package within the importing source file. It is declared in the file block.

If the PackageName is omitted, it defaults to the identifier specified in the package clause of the imported package.

If an explicit period (.) appears instead of a name, all the package’s exported identifiers declared in that package’s package block will be declared in the importing source file’s file block and must be accessed without a qualifier.

The interpretation of the ImportPath is implementation-dependent but it is typically a substring of the full file name of the compiled package and may be relative to a repository of installed packages.

Implementation restriction: A compiler may restrict ImportPaths to non-empty strings using only characters belonging to Unicode’s L, M, N, P, and S general categories (the Graphic characters without spaces) and may also exclude the characters !"#$%&'()*,:;<=>?[\]^{|}` and the Unicode replacement character U+FFFD.

Consider a compiled a package containing the package clause package math, which exports function Sin, and installed the compiled package in the file identified by "lib/math". This table illustrates how Sin is accessed in files that import the package after the various types of import declaration.

Import declaration          Local name of Sinimport   "lib/math"         math.Sin
import m "lib/math"         m.Sin
import . "lib/math"         Sin

An import declaration declares a dependency relation between the importing and imported package. It is illegal for a package to import itself, directly or indirectly, or to directly import a package without referring to any of its exported identifiers. To import a package solely for its side-effects (initialization), use the blank identifier as explicit package name:

import _ "lib/math"

  1. 核心概念:包的初始化(Side Effects)
  • Go 的包可以包含 init() 函數,它會在包被導入時自動執行,通常用于:
    • 注冊驅動(如數據庫驅動 import _ "github.com/go-sql-driver/mysql")。
    • 初始化全局配置或資源。
    • 執行一些只需運行一次的代碼(如日志系統初始化)。
  • 這種初始化行為被稱為包的 “Side Effects”(副作用)

  1. 空白標識符 _ 的作用
  • 正常情況下,如果導入一個包但未使用它,Go 編譯器會報錯(避免無用導入)。
  • 通過 _ 顯式忽略包名,告訴編譯器:
    “我只需要執行這個包的 init 函數,不需要調用它的其他功能。”

An example package

Here is a complete Go package that implements a concurrent prime sieve.

package mainimport "fmt"// Send the sequence 2, 3, 4, … to channel 'ch'.
func generate(ch chan<- int) {for i := 2; ; i++ {ch <- i  // Send 'i' to channel 'ch'.}
}// Copy the values from channel 'src' to channel 'dst',
// removing those divisible by 'prime'.
func filter(src <-chan int, dst chan<- int, prime int) {for i := range src {  // Loop over values received from 'src'.if i%prime != 0 {dst <- i  // Send 'i' to channel 'dst'.}}
}// The prime sieve: Daisy-chain filter processes together.
func sieve() {ch := make(chan int)  // Create a new channel.go generate(ch)       // Start generate() as a subprocess.for {prime := <-chfmt.Print(prime, "\n")ch1 := make(chan int)go filter(ch, ch1, prime)ch = ch1}
}func main() {sieve()
}

Program initialization and execution

The zero value

When storage is allocated for a variable, either through a declaration or a call of new, or when a new value is created, either through a composite literal or a call of make, and no explicit initialization is provided, the variable or value is given a default value. Each element of such a variable or value is set to the zero value for its type: false for booleans, 0 for numeric types, "" for strings, and nil for pointers, functions, interfaces, slices, channels, and maps. This initialization is done recursively, so for instance each element of an array of structs will have its fields zeroed if no value is specified.

These two simple declarations are equivalent:

var i int
var i int = 0

After

type T struct { i int; f float64; next *T }
t := new(T)

the following holds:

t.i == 0
t.f == 0.0
t.next == nil

The same would also be true after

var t T

Package initialization

Within a package, package-level variable initialization proceeds stepwise, with each step selecting the variable earliest in declaration order which has no dependencies on uninitialized variables.

在包內部,包級變量的初始化是分步進行的。每一步會選擇聲明順序中最早且不依賴于任何未初始化變量的變量進行初始化。

More precisely, a package-level variable is considered ready for initialization if it is not yet initialized and either has no initialization expression or its initialization expression has no dependencies on uninitialized variables. Initialization proceeds by repeatedly initializing the next package-level variable that is earliest in declaration order and ready for initialization, until there are no variables ready for initialization.

更準確地說,一個包級變量在以下情況下被認為“已準備好初始化”:它尚未被初始化,并且(1)沒有初始化表達式,或(2)其初始化表達式不依賴于任何未初始化的變量。初始化過程會反復選擇聲明順序中最早且“已準備好初始化”的包級變量進行初始化,直到沒有變量滿足這一條件為止。

If any variables are still uninitialized when this process ends, those variables are part of one or more initialization cycles, and the program is not valid.

Multiple variables on the left-hand side of a variable declaration initialized by single (multi-valued) expression on the right-hand side are initialized together: If any of the variables on the left-hand side is initialized, all those variables are initialized in the same step.

var x = a
var a, b = f() // a and b are initialized together, before x is initialized

For the purpose of package initialization, blank variables are treated like any other variables in declarations.

The declaration order of variables declared in multiple files is determined by the order in which the files are presented to the compiler: Variables declared in the first file are declared before any of the variables declared in the second file, and so on. To ensure reproducible initialization behavior, build systems are encouraged to present multiple files belonging to the same package in lexical file name order to a compiler.

Dependency analysis does not rely on the actual values of the variables, only on lexical references to them in the source, analyzed transitively.

依賴分析不依賴于變量的實際值,而僅依賴于源代碼中對它們的詞法引用(且會進行傳遞性分析)。

For instance, if a variable x’s initialization expression refers to a function whose body refers to variable y then x depends on y. Specifically:

  • A reference to a variable or function is an identifier denoting that variable or function.
  • A reference to a method m is a method value or method expression of the form t.m, where the (static) type of t is not an interface type, and the method m is in the method set of t. It is immaterial【不重要】 whether the resulting function value t.m is invoked.
  • A variable, function, or method x depends on a variable y if x’s initialization expression or body (for functions and methods) contains a reference to y or to a function or method that depends on y.

For example, given the declarations

var (a = c + b  // == 9b = f()    // == 4c = f()    // == 5d = 3      // == 5 after initialization has finished
)func f() int {d++return d
}

the initialization order is d, b, c, a. Note that the order of subexpressions in initialization expressions is irrelevant: a = c + b and a = b + c result in the same initialization order in this example.

Dependency analysis is performed per package; only references referring to variables, functions, and (non-interface) methods declared in the current package are considered. If other, hidden, data dependencies exists between variables, the initialization order between those variables is unspecified.

For instance, given the declarations

var x = I(T{}).ab()   // x has an undetected, hidden dependency on a and b
var _ = sideEffect()  // unrelated to x, a, or b
var a = b
var b = 42type I interface      { ab() []int }
type T struct{}
func (T) ab() []int   { return []int{a, b} }

the variable a will be initialized after b but whether x is initialized before b, between b and a, or after a, and thus also the moment at which sideEffect() is called (before or after x is initialized) is not specified.

Variables may also be initialized using functions named init declared in the package block, with no arguments and no result parameters.

func init() {}

Multiple such functions may be defined per package, even within a single source file. In the package block, the init identifier can be used only to declare init functions, yet the identifier itself is not declared. Thus init functions cannot be referred to from anywhere in a program.

The entire package is initialized by assigning initial values to all its package-level variables followed by calling all init functions in the order they appear in the source, possibly in multiple files, as presented to the compiler.

Program initialization

The packages of a complete program are initialized stepwise, one package at a time. If a package has imports, the imported packages are initialized before initializing the package itself. If multiple packages import a package, the imported package will be initialized only once. The importing of packages, by construction, guarantees that there can be no cyclic initialization dependencies. More precisely:

Given the list of all packages, sorted by import path, in each step the first uninitialized package in the list for which all imported packages (if any) are already initialized is initialized. This step is repeated until all packages are initialized.

Package initialization—variable initialization and the invocation of init functions—happens in a single goroutine, sequentially, one package at a time. An init function may launch other goroutines, which can run concurrently with the initialization code. However, initialization always sequences the init functions: it will not invoke the next one until the previous one has returned.

Program execution

A complete program is created by linking a single, unimported package called the main package with all the packages it imports, transitively. The main package must have package name main and declare a function main that takes no arguments and returns no value.

func main() {}

Program execution begins by initializing the program and then invoking the function main in package main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.

Errors

The predeclared type error is defined as

type error interface {Error() string
}

It is the conventional interface for representing an error condition, with the nil value representing no error. For instance, a function to read data from a file might be defined:

func Read(f *File, b []byte) (n int, err error)

Run-time panics

Execution errors such as attempting to index an array out of bounds trigger a run-time panic equivalent to a call of the built-in function panic with a value of the implementation-defined interface type runtime.Error. That type satisfies the predeclared interface type error. The exact error values that represent distinct run-time error conditions are unspecified.

package runtimetype Error interface {error// and perhaps other methods
}

System considerations

Package unsafe

The built-in package unsafe, known to the compiler and accessible through the import path "unsafe", provides facilities for low-level programming including operations that violate the type system. A package using unsafe must be vetted manually for type safety and may not be portable. The package provides the following interface:

package unsafetype ArbitraryType int  // shorthand for an arbitrary Go type; it is not a real type
type Pointer *ArbitraryTypefunc Alignof(variable ArbitraryType) uintptr
func Offsetof(selector ArbitraryType) uintptr
func Sizeof(variable ArbitraryType) uintptrtype IntegerType int  // shorthand for an integer type; it is not a real type
func Add(ptr Pointer, len IntegerType) Pointer
func Slice(ptr *ArbitraryType, len IntegerType) []ArbitraryType
func SliceData(slice []ArbitraryType) *ArbitraryType
func String(ptr *byte, len IntegerType) string
func StringData(str string) *byte

A Pointer is a pointer type but a Pointer value may not be dereferenced. Any pointer or value of core type uintptr can be converted to a type of core type Pointer and vice versa. The effect of converting between Pointer and uintptr is implementation-defined.

var f float64
bits = *(*uint64)(unsafe.Pointer(&f))type ptr unsafe.Pointer
bits = *(*uint64)(ptr(&f))func f[P ~*B, B any](p P) uintptr {return uintptr(unsafe.Pointer(p))
}var p ptr = nil

The functions Alignof and Sizeof take an expression x of any type and return the alignment or size, respectively, of a hypothetical variable v as if v were declared via var v = x.

The function Offsetof takes a (possibly parenthesized) selector s.f, denoting a field f of the struct denoted by s or *s, and returns the field offset in bytes relative to the struct’s address. If f is an embedded field, it must be reachable without pointer indirections through fields of the struct. For a struct s with field f:

uintptr(unsafe.Pointer(&s)) + unsafe.Offsetof(s.f) == uintptr(unsafe.Pointer(&s.f))

Computer architectures may require memory addresses to be aligned; that is, for addresses of a variable to be a multiple of a factor, the variable’s type’s alignment. The function Alignof takes an expression denoting a variable of any type and returns the alignment of the (type of the) variable in bytes. For a variable x:

uintptr(unsafe.Pointer(&x)) % unsafe.Alignof(x) == 0

A (variable of) type T has variable size if T is a type parameter, or if it is an array or struct type containing elements or fields of variable size. Otherwise the size is constant. Calls to Alignof, Offsetof, and Sizeof are compile-time constant expressions of type uintptr if their arguments (or the struct s in the selector expression s.f for Offsetof) are types of constant size.

The function Add adds len to ptr and returns the updated pointer unsafe.Pointer(uintptr(ptr) + uintptr(len)) [Go 1.17]. The len argument must be of integer type or an untyped constant. A constant len argument must be representable by a value of type int; if it is an untyped constant it is given type int. The rules for valid uses of Pointer still apply.

The function Slice returns a slice whose underlying array starts at ptr and whose length and capacity are len. Slice(ptr, len) is equivalent to

(*[len]ArbitraryType)(unsafe.Pointer(ptr))[:]

except that, as a special case, if ptr is nil and len is zero, Slice returns nil [Go 1.17].

The len argument must be of integer type or an untyped constant. A constant len argument must be non-negative and representable by a value of type int; if it is an untyped constant it is given type int. At run time, if len is negative, or if ptr is nil and len is not zero, a run-time panic occurs [Go 1.17].

The function SliceData returns a pointer to the underlying array of the slice argument. If the slice’s capacity cap(slice) is not zero, that pointer is &slice[:1][0]. If slice is nil, the result is nil. Otherwise it is a non-nil pointer to an unspecified memory address [Go 1.20].

The function String returns a string value whose underlying bytes start at ptr and whose length is len. The same requirements apply to the ptr and len argument as in the function Slice. If len is zero, the result is the empty string "". Since Go strings are immutable, the bytes passed to String must not be modified afterwards. [Go 1.20]

The function StringData returns a pointer to the underlying bytes of the str argument. For an empty string the return value is unspecified, and may be nil. Since Go strings are immutable, the bytes returned by StringData must not be modified [Go 1.20].

Size and alignment guarantees

For the numeric types, the following sizes are guaranteed:

type                                 size in bytesbyte, uint8, int8                     1
uint16, int16                         2
uint32, int32, float32                4
uint64, int64, float64, complex64     8
complex128                           16

The following minimal alignment properties are guaranteed:

  1. For a variable x of any type: unsafe.Alignof(x) is at least 1.
  2. For a variable x of struct type: unsafe.Alignof(x) is the largest of all the values unsafe.Alignof(x.f) for each field f of x, but at least 1.
  3. For a variable x of array type: unsafe.Alignof(x) is the same as the alignment of a variable of the array’s element type.

A struct or array type has size zero if it contains no fields (or elements, respectively) that have a size greater than zero. Two distinct zero-size variables may have the same address in memory.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/76514.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/76514.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/76514.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Python Cookbook-5.1 對字典排序

任務 你想對字典排序。這可能意味著需要先根據字典的鍵排序&#xff0c;然后再讓對應值也處于同樣的順序。 解決方案 最簡單的方法可以通過這樣的描述來概括:先將鍵排序&#xff0c;然后由此選出對應值: def sortedDictValues(adict):keys adict.keys()keys.sort()return …

Git Rebase 操作中丟失提交的恢復方法

背景介紹 在團隊協作中,使用 Git 進行版本控制是常見實踐。然而,有時在執行 git rebase 或者其他操作后,我們可能會發現自己的提交記錄"消失"了,這往往讓開發者感到恐慌。本文將介紹幾種在 rebase 后恢復丟失提交的方法。 問題描述 當我們執行以下操作時,可能…

C語言基礎要素(019):輸出ASCII碼表

計算機以二進制處理信息&#xff0c;但二進制對人類并不友好。比如說我們規定用二進制值 01000001 表示字母’A’&#xff0c;顯然通過鍵盤輸入或屏幕閱讀此數據而理解它為字母A&#xff0c;是比較困難的。為了有效的使用信息&#xff0c;先驅者們創建了一種稱為ASCII碼的交換代…

鴻蒙定位開發服務

引言 鴻蒙操作系統&#xff08;HarmonyOS&#xff09;作為面向萬物互聯時代的分布式操作系統&#xff0c;其定位服務&#xff08;Location Kit&#xff09;為開發者提供了多場景、高精度的位置能力支持。本文將從技術原理、開發流程到實戰案例&#xff0c;全面解析鴻蒙定位服務…

rknn_convert的使用方法

rknn_convert是RKNN-Toolkit2提供的一套常用模型轉換工具&#xff0c;通過封裝上述API接口&#xff0c;用戶只需編輯模型對應的yml配置文件&#xff0c;就可以通過指令轉換模型。以下是如何使用rknn_convert工具的示例命令以及支持的指令參數&#xff1a; python -m rknn.api.…

解決 axios get請求瞎轉義問題

在Vue.js項目中&#xff0c;axios 是一個常用的HTTP客戶端庫&#xff0c;用于發送HTTP請求。qs 是一個用于處理查詢字符串的庫&#xff0c;通常與 axios 結合使用&#xff0c;特別是在處理POST請求時&#xff0c;將對象序列化為URL編碼的字符串。 1. 安裝 axios 和 qs 首先&a…

【XTerminal】【樹莓派】Linux系統下的函數調用編程

目錄 一、XTerminal下的Linux系統調用編程 1.1理解進程和線程的概念并在Linux系統下完成相應操作 (1) 進程 (2)線程 (3) 進程 vs 線程 (4)Linux 下的實踐操作 1.2Linux的“虛擬內存管理”和stm32正式物理內存&#xff08;內存映射&#xff09;的區別 (1)Linux虛擬內存管…

torch 拆分子張量 分割張量

目錄 unbind拆分子張量 1. 沿著第n個維度拆分&#xff08;即按“批次”拆分&#xff09; split分割張量 常用用法&#xff1a; 總結&#xff1a; unbind拆分子張量 import torchquaternions torch.tensor([[1, 2, 3, 4], [5, 6, 7, 8]]) result torch.unbind(quaternio…

【Linux】內核驅動學習筆記(二)

7、framebuffer驅動詳解 7.1、什么是framebuffer (1)裸機中如何操作LCD (2)OS下操作LCD的難點 (3)framebuffer幀緩沖&#xff08;簡稱fb&#xff09;是linux內核中虛擬出的一個設備 (4)framebuffer向應用層提供一個統一標準接口的顯示設備 (5)從驅動來看&#xff0c;fb是一個…

用 Docker Compose 與 Nginx 反向代理部署 Vikunja 待辦事項管理系統

在高效管理日常任務和項目的過程中&#xff0c;開源待辦事項工具 Vikunja 以其簡潔、直觀的設計和多視圖支持受到越來越多用戶的青睞。本文將詳細介紹如何使用 Docker Compose 快速部署 Vikunja&#xff0c;并通過 Nginx 反向代理實現 HTTPS 訪問&#xff0c;從而確保服務安全穩…

使用Python快速接入DeepSeek API的步驟指南

使用Python快速接入DeepSeek API的步驟指南 1. 前期準備 注冊DeepSeek賬號 訪問DeepSeek官網注冊賬號 完成郵箱驗證等認證流程 獲取API密鑰 登錄后進入控制臺 → API管理 創建新的API Key并妥善保存 安裝必要庫 pip install requests # 可選&#xff1a;處理復雜場景 pip…

Redis 主要能夠用來做什么

Redis&#xff08;Remote Dictionary Server&#xff09;是一種基于內存的鍵值存儲數據庫&#xff0c;它的性能極高&#xff0c;廣泛應用于各種高并發場景。以下是 Redis 常見的用途&#xff1a; 1. 緩存&#xff08;Cache&#xff09; 作用&#xff1a;存儲熱點數據&#xf…

印度股票實時數據API接口選型指南:iTick.org如何成為開發者優選

在全球金融數字化浪潮中&#xff0c;印度股票市場因其高速增長潛力備受關注。對于量化交易開發者、金融科技公司而言&#xff0c;穩定可靠的股票報價API接口是獲取市場數據的核心基礎設施。本文將深度對比主流印度股票API&#xff0c;并揭示iTick在數據服務領域的獨特優勢。 一…

24.多路轉接-poll

poll也是一種linux中的多路轉接的方案 解決select的fd有上限的問題解決select每次調用都要重新設置關心的fd poll函數接口 poll, ppoll - wait for some event on a file descriptor#include <poll.h>int poll(struct pollfd *fds, nfds_t nfds, int timeout);DESCRIP…

Linux 基礎入門操作 前言 linux操作指令介紹

1 linux 目錄介紹 Linux 文件系統采用層次化的目錄結構&#xff0c;所有目錄都從根目錄 / 開始 1.1 核心目錄 / (根目錄) 整個文件系統的起點、包含所有其他目錄和文件 /bin (基本命令二進制文件) 存放系統最基本的shell命令&#xff1a;如 ls, cp, mv, rm, cat 等&#…

Chrome開發者工具實戰:調試三劍客

在前端開發的世界里&#xff0c;Chrome開發者工具就是我們的瑞士軍刀&#xff0c;它集成了各種強大的功能&#xff0c;幫助我們快速定位和解決代碼中的問題。今天&#xff0c;就讓我們一起來看看如何使用Chrome開發者工具中的“調試三劍客”&#xff1a;斷點調試、調用棧跟蹤和…

函數柯里化(Currying)介紹(一種將接受多個參數的函數轉換為一系列接受單一參數的函數的技術)

文章目錄 柯里化的特點示例普通函數柯里化實現使用Lodash進行柯里化 應用場景總結 函數柯里化&#xff08;Currying&#xff09;是一種將接受多個參數的函數轉換為一系列接受單一參數的函數的技術。換句話說&#xff0c;柯里化將一個多參數函數轉化為一系列嵌套的單參數函數。 …

torch.nn中的非線性激活介紹合集——Pytorch中的非線性激活

1、nn.ELU 基本語法&#xff1a; class torch.nn.ELU(alpha1.0, inplaceFalse)按元素應用 Exponential Linear Unit &#xff08;ELU&#xff09; 函數。 論文中描述的方法&#xff1a;通過指數線性單元 &#xff08;ELU&#xff09; 進行快速準確的深度網絡學習。 ELU 定義為…

Databend Cloud Dashboard 全新升級:直擊痛點,釋放數據價值

自 Databend Cloud 上線以來&#xff0c;我們一直致力于為用戶提供高效的數據處理與可視化體驗。早期&#xff0c;我們在工作區的“圖表”區域推出了輕量級可視化功能&#xff0c;支持積分卡、餅圖、柱狀圖和折線圖四種展示方式。這些功能簡單易用&#xff0c;基本滿足了用戶對…

Android Fresco 框架擴展模塊源碼深度剖析(四)

Android Fresco 框架擴展模塊源碼深度剖析 一、引言 在 Android 開發領域&#xff0c;圖片處理一直是一個重要且具有挑戰性的任務。Fresco 作為 Facebook 開源的強大圖片加載框架&#xff0c;在圖片的加載、緩存和顯示等方面已經提供了非常完善的功能。然而&#xff0c;為了滿…