Go 語言規范學習(3)

文章目錄

    • Properties of types and values
      • Representation of values
      • Underlying types【底層類型】
      • Core types【核心類型】
      • Type identity
      • Assignability
      • Representability
      • Method sets
    • Blocks
    • Declarations and scope
      • Label scopes
      • Blank identifier
      • Predeclared identifiers
      • Exported identifiers
      • Uniqueness of identifiers
      • Constant declarations
      • Iota
      • Type declarations
        • Alias declarations
        • Type definitions
      • Type parameter declarations
        • Type constraints
        • Satisfying a type constraint
      • Variable declarations
      • Short variable declarations
      • Function declarations
      • Method declarations

Properties of types and values

Representation of values

值的表示。重要!

Values of predeclared types (see below for the interfaces any and error), arrays, and structs are self-contained: Each such value contains a complete copy of all its data, and variables of such types store the entire value. For instance, an array variable provides the storage (the variables) for all elements of the array. The respective zero values are specific to the value’s types; they are never nil.

Non-nil pointer, function, slice, map, and channel values contain references to underlying data which may be shared by multiple values:

  • A pointer value is a reference to the variable holding the pointer base type value.
  • A function value contains references to the (possibly anonymous) function and enclosed variables.
  • A slice value contains the slice length, capacity, and a reference to its underlying array.
  • A map or channel value is a reference to the implementation-specific data structure of the map or channel.

An interface value may be self-contained or contain references to underlying data depending on the interface’s dynamic type. The predeclared identifier nil is the zero value for types whose values can contain references.

When multiple values share underlying data, changing one value may change another. For instance, changing an element of a slice will change that element in the underlying array for all slices that share the array.

Underlying types【底層類型】

Each type T has an underlying type: If T is one of the predeclared boolean, numeric, or string types, or a type literal【類型字面值】, the corresponding underlying type is T itself. Otherwise, T’s underlying type is the underlying type of the type to which T refers in its declaration.

注意:因為接口類型在類型字面值中,所有接口類型的底層類型是接口自身!

For a type parameter that is the underlying type of its type constraint, which is always an interface.

類型參數的底層類型=類型約束的底層類型。由于類型約束是一個接口類型,所以類型參數的底層類型也是接口類型。

type (A1 = stringA2 = A1
)type (B1 stringB2 B1B3 []B1B4 B3
)func f[P any](x P) {}

The underlying type of string, A1, A2, B1, and B2 is string. The underlying type of []B1, B3, and B4 is []B1. The underlying type of P is interface{}.

注意:B3 的底層類型是 []B1 而不是 []string,因為 []B1 是類型字面值!

類型參數 P 的底層類型是 any 的底層類型,any 的底層類型是 interface{}。

Core types【核心類型】

Each non-interface type T has a core type, which is the same as the underlying type of T.

每個非接口類型都有一個核心類型,接口類型不一定有一個核心類型。

An interface T has a core type if one of the following conditions is satisfied:

  1. There is a single type U which is the underlying type of all types in the type set of T; or
  2. the type set of T contains only channel types with identical element type E, and all directional channels have the same direction.

No other interfaces have a core type.

The core type of an interface is, depending on the condition that is satisfied, either:

  1. the type U; or
  2. the type chan E if T contains only bidirectional channels, or the type chan<- E or <-chan E depending on the direction of the directional channels present.

By definition, a core type is never a defined type, type parameter, or interface type.


By construction, an interface’s type set never contains an interface type.

對于非接口類型,它的核心類型是底層類型,而底層類型不可能是定義的類型,類型參數和接口類型。

對于接口類型,因為接口類型的類型集里不會包含一個接口類型,所以接口類型的核心類型不可能是接口類型。接口類型集里所有類型的底層類型也不可能是定義的類型和類型參數。


Examples of interfaces with core types:

type Celsius float32
type Kelvin  float32interface{ int }                          // int
interface{ Celsius|Kelvin }               // float32
interface{ ~chan int }                    // chan int
interface{ ~chan int|~chan<- int }        // chan<- int
interface{ ~[]*data; String() string }    // []*data

Examples of interfaces without core types:

interface{}                               // no single underlying type
interface{ Celsius|float64 }              // no single underlying type
interface{ chan int | chan<- string }     // channels have different element types
interface{ <-chan int | chan<- int }      // directional channels have different directions

Some operations (slice expressions, append and copy) rely on a slightly more loose form of core types which accept byte slices and strings. Specifically, if there are exactly two types, []byte and string, which are the underlying types of all types in the type set of interface T, the core type of T is called bytestring.

Examples of interfaces with bytestring core types:

interface{ int }                          // int (same as ordinary core type)
interface{ []byte | string }              // bytestring
interface{ ~[]byte | myString }           // bytestring

Note that bytestring is not a real type; it cannot be used to declare variables or compose other types. It exists solely to describe the behavior of some operations that read from a sequence of bytes, which may be a byte slice or a string.

Type identity

Two types are either identical or different.

A named type is always different from any other type.


Predeclared types, defined types, and type parameters are called named types【命名類型】. An alias denotes a named type if the type given in the alias declaration is a named type.【注意:類型字面值不是命名類型,比如[]int、struct{} 等是未命名類型】

byte類型和uint8類型是相同的類型,rune類型和int32類型是相同的類型。


Otherwise, two types are identical if their underlying type literals are structurally equivalent; that is, they have the same literal structure and corresponding components have identical types. In detail:

  • Two array types are identical if they have identical element types and the same array length.
  • Two slice types are identical if they have identical element types.
  • Two struct types are identical if they have the same sequence of fields, and if corresponding pairs of fields have the same names, identical types, and identical tags, and are either both embedded or both not embedded. Non-exported field names from different packages are always different.

// 包 pkg1
type S1 struct {name stringAge  int
}
// 包 pkg2
type S2 struct {name stringAge  int
}

S1S2 不是相同類型,因為它們的非導出字段 name 來自不同包,被視為不同字段。


  • Two pointer types are identical if they have identical base types.
  • Two function types are identical if they have the same number of parameters and result values, corresponding parameter and result types are identical, and either both functions are variadic or neither is. Parameter and result names are not required to match.
  • Two interface types are identical if they define the same type set.
  • Two map types are identical if they have identical key and element types.
  • Two channel types are identical if they have identical element types and the same direction.
  • Two instantiated types are identical if their defined types and all type arguments are identical.

Given the declarations

type (A0 = []stringA1 = A0A2 = struct{ a, b int }A3 = intA4 = func(A3, float64) *A0A5 = func(x int, _ float64) *[]stringB0 A0B1 []stringB2 struct{ a, b int }B3 struct{ a, c int }B4 func(int, float64) *B0B5 func(x int, y float64) *A1C0 = B0D0[P1, P2 any] struct{ x P1; y P2 }E0 = D0[int, string]
)

these types are identical:

A0, A1, and []string
A2 and struct{ a, b int }
A3 and int
A4, func(int, float64) *[]string, and A5B0 and C0
D0[int, string] and E0
[]int and []int
struct{ a, b *B5 } and struct{ a, b *B5 }
func(x int, y float64) *[]string, func(int, float64) (result *[]string), and A5

B0 and B1 are different because they are new types created by distinct type definitions; 【定義的類型是命名的類型,都和其他類型不同】

func(int, float64) *B0 and func(x int, y float64) *[]string are different because B0 is different from []string; 【B0是一個定義的類型,和其他類型都不同】

and P1 and P2 are different because they are different type parameters. 【類型參數是命名的類型,和其他類型都不同】

D0[int, string] and struct{ x int; y string } are different because the former is an instantiated defined type while the latter is a type literal (but they are still assignable). 【Instantiating a type results in a new non-generic named type。實例化后的類型是一個新的命名的類型,都和其他類型不同】

Assignability

A value x of type V is assignable to a variable of type T (“x is assignable to T”) if one of the following conditions applies【值x的類型是V,變量的類型是T,V和T可能是類型參數】:

  • V and T are identical.
  • V and T have identical underlying types but are not type parameters and at least one of V or T is not a named type. 【 V 和 T 的底層類型(underlying types)相同,且 V 和 T 都不是類型參數(type parameters),并且至少有一個不是命名類型(named type)。】

在賦值規則中,類型參數不能直接參與底層類型的比較,因為:

  • 類型參數的具體類型在編譯時才能確定,無法在賦值時靜態判斷其底層類型是否匹配。
  • 泛型的類型約束(Constraint)已經確保了類型參數的有效性,因此不需要在賦值階段額外檢查。

  • V and T are channel types with identical element types, V is a bidirectional channel, and at least one of V or T is not a named type.
  • T is an interface type, but not a type parameter, and x implements T. 【變量的類型是接口類型,值x實現了這個接口】
  • x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type, but not a type parameter.
  • x is an untyped constant representable by a value of type T.

Additionally, if x’s type V or T are type parameters, x is assignable to a variable of type T if one of the following conditions applies:

  • x is the predeclared identifier nil, T is a type parameter, and x is assignable to each type in T’s type set.
  • V is not a named type, T is a type parameter, and x is assignable to each type in T’s type set. 【??x 怎么可以賦值給多個類型??】
  • V is a type parameter and T is not a named type, and values of each type in V’s type set are assignable to T.

這里不給舉個例子,看不明白

Representability

A constant x is representable by a value of type T, where T is not a type parameter, if one of the following conditions applies:

  • x is in the set of values determined by T.
  • T is a floating-point type and x can be rounded to T’s precision without overflow. Rounding uses IEEE 754 round-to-even rules but with an IEEE negative zero further simplified to an unsigned zero. Note that constant values never result in an IEEE negative zero, NaN, or infinity.
  • T is a complex type, and x’s components real(x) and imag(x) are representable by values of T’s component type (float32 or float64).

If T is a type parameter, x is representable by a value of type T if x is representable by a value of each type in T’s type set. 【??這里也不給舉個例子,無語。。。】

x                   T           x is representable by a value of T because'a'                 byte        97 is in the set of byte values
97                  rune        rune is an alias for int32, and 97 is in the set of 32-bit integers
"foo"               string      "foo" is in the set of string values
1024                int16       1024 is in the set of 16-bit integers
42.0                byte        42 is in the set of unsigned 8-bit integers
1e10                uint64      10000000000 is in the set of unsigned 64-bit integers
2.718281828459045   float32     2.718281828459045 rounds to 2.7182817 which is in the set of float32 values
-1e-1000            float64     -1e-1000 rounds to IEEE -0.0 which is further simplified to 0.0
0i                  int         0 is an integer value
(42 + 0i)           float32     42.0 (with zero imaginary part) is in the set of float32 values
x                   T           x is not representable by a value of T because0                   bool        0 is not in the set of boolean values
'a'                 string      'a' is a rune, it is not in the set of string values
1024                byte        1024 is not in the set of unsigned 8-bit integers
-1                  uint16      -1 is not in the set of unsigned 16-bit integers
1.1                 int         1.1 is not an integer value
42i                 float32     (0 + 42i) is not in the set of float32 values
1e1000              float64     1e1000 overflows to IEEE +Inf after rounding

Method sets

The method set of a type determines the methods that can be called on an operand of that type. Every type has a (possibly empty) method set associated with it:

  • The method set of a defined type T consists of all methods declared with receiver type T.
  • The method set of a pointer to a defined type T (where T is neither a pointer nor an interface) is the set of all methods declared with receiver *T or T.
  • The method set of an interface type is the intersection of the method sets of each type in the interface’s type set (the resulting method set is usually just the set of declared methods in the interface).

Further rules apply to structs (and pointer to structs) containing embedded fields, as described in the section on struct types. Any other type has an empty method set.

In a method set, each method must have a unique non-blank method name.

Blocks

A block is a possibly empty sequence of declarations and statements within matching brace brackets.

Block         = "{" StatementList "}" .
StatementList = { Statement ";" } .

In addition to explicit blocks in the source code, there are implicit blocks:

  1. The universe block encompasses all Go source text.
  2. Each package has a package block containing all Go source text for that package.
  3. Each file has a file block containing all Go source text in that file.
  4. Each “if”, “for”, and “switch” statement is considered to be in its own implicit block.
  5. Each clause in a “switch” or “select” statement acts as an implicit block.

Blocks nest and influence scoping.

Declarations and scope

重要!

A declaration binds a non-blank identifier to a constant, type, type parameter, variable, function, label, or package. Every identifier in a program must be declared. No identifier may be declared twice in the same block, and no identifier may be declared in both the file and package block.

The blank identifier may be used like any other identifier in a declaration, but it does not introduce a binding and thus is not declared.

In the package block, the identifier init may only be used for init function declarations, and like the blank identifier it does not introduce a new binding.

Declaration  = ConstDecl | TypeDecl | VarDecl .
TopLevelDecl = Declaration | FunctionDecl | MethodDecl .

The scope of a declared identifier is the extent of source text in which the identifier denotes the specified constant, type, variable, function, label, or package.

Go is lexically scoped using blocks:

  1. The scope of a predeclared identifier is the universe block.
  2. The scope of an identifier denoting a constant, type, variable, or function (but not method) declared at top level (outside any function) is the package block.
  3. The scope of the package name of an imported package is the file block of the file containing the import declaration.
  4. The scope of an identifier denoting a method receiver, function parameter, or result variable is the function body.
  5. The scope of an identifier denoting a type parameter of a function or declared by a method receiver begins after the name of the function and ends at the end of the function body.
  6. The scope of an identifier denoting a type parameter of a type begins after the name of the type and ends at the end of the TypeSpec.
  7. The scope of a constant or variable identifier declared inside a function begins at the end of the ConstSpec or VarSpec (ShortVarDecl for short variable declarations) and ends at the end of the innermost containing block.
  8. The scope of a type identifier declared inside a function begins at the identifier in the TypeSpec and ends at the end of the innermost containing block.

An identifier declared in a block may be redeclared in an inner block. While the identifier of the inner declaration is in scope, it denotes the entity declared by the inner declaration.

The package clause is not a declaration; the package name does not appear in any scope. Its purpose is to identify the files belonging to the same package and to specify the default package name for import declarations.

Label scopes

Labels are declared by labeled statements and are used in the “break”, “continue”, and “goto” statements. It is illegal to define a label that is never used. In contrast to other identifiers, labels are not block scoped and do not conflict with identifiers that are not labels. The scope of a label is the body of the function in which it is declared and excludes the body of any nested function.

Blank identifier

The blank identifier is represented by the underscore character _. It serves as an anonymous placeholder instead of a regular (non-blank) identifier and has special meaning in declarations, as an operand, and in assignment statements.

Predeclared identifiers

The following identifiers are implicitly declared in the universe block [Go 1.18] [Go 1.21]:

Types:any bool byte comparablecomplex64 complex128 error float32 float64int int8 int16 int32 int64 rune stringuint uint8 uint16 uint32 uint64 uintptrConstants:true false iotaZero value:nilFunctions:append cap clear close complex copy delete imag lenmake max min new panic print println real recover

Exported identifiers

An identifier may be exported to permit access to it from another package. An identifier is exported if both:

  1. the first character of the identifier’s name is a Unicode uppercase letter (Unicode character category Lu); and
  2. the identifier is declared in the package block or it is a field name or method name.

All other identifiers are not exported.

Uniqueness of identifiers

Given a set of identifiers, an identifier is called unique if it is different from every other in the set. Two identifiers are different if they are spelled differently, or if they appear in different packages and are not exported. Otherwise, they are the same.

不同包里定義的標識符即時名字一樣也是不同的。

Constant declarations

A constant declaration binds a list of identifiers (the names of the constants) to the values of a list of constant expressions. The number of identifiers must be equal to the number of expressions, and the nth identifier on the left is bound to the value of the nth expression on the right.

ConstDecl      = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
ConstSpec      = IdentifierList [ [ Type ] "=" ExpressionList ] .IdentifierList = identifier { "," identifier } .
ExpressionList = Expression { "," Expression } .

If the type is present, all constants take the type specified, and the expressions must be assignable to that type, which must not be a type parameter. If the type is omitted, the constants take the individual types of the corresponding expressions. If the expression values are untyped constants, the declared constants remain untyped and the constant identifiers denote the constant values. For instance, if the expression is a floating-point literal, the constant identifier denotes a floating-point constant, even if the literal’s fractional part is zero.

const Pi float64 = 3.14159265358979323846
const zero = 0.0         // untyped floating-point constant
const (size int64 = 1024eof        = -1  // untyped integer constant
)
const a, b, c = 3, 4, "foo"  // a = 3, b = 4, c = "foo", untyped integer and string constants
const u, v float32 = 0, 3    // u = 0.0, v = 3.0

Within a parenthesized const declaration list the expression list may be omitted from any but the first ConstSpec. Such an empty list is equivalent to the textual substitution of the first preceding non-empty expression list and its type if any. Omitting the list of expressions is therefore equivalent to repeating the previous list. The number of identifiers must be equal to the number of expressions in the previous list. Together with the iota constant generator this mechanism permits light-weight declaration of sequential values:

const (Sunday = iotaMondayTuesdayWednesdayThursdayFridayPartydaynumberOfDays  // this constant is not exported
)

Iota

Within a constant declaration, the predeclared identifier iota represents successive【連續的】 untyped integer constants. Its value is the index of the respective ConstSpec in that constant declaration, starting at zero. It can be used to construct a set of related constants:

const (c0 = iota  // c0 == 0c1 = iota  // c1 == 1c2 = iota  // c2 == 2
)const (a = 1 << iota  // a == 1  (iota == 0)b = 1 << iota  // b == 2  (iota == 1)c = 3          // c == 3  (iota == 2, unused)d = 1 << iota  // d == 8  (iota == 3)
)const (u         = iota * 42  // u == 0     (untyped integer constant)v float64 = iota * 42  // v == 42.0  (float64 constant)w         = iota * 42  // w == 84    (untyped integer constant)
)const x = iota  // x == 0
const y = iota  // y == 0

By definition, multiple uses of iota in the same ConstSpec all have the same value:

const (bit0, mask0 = 1 << iota, 1<<iota - 1  // bit0 == 1, mask0 == 0  (iota == 0)bit1, mask1                           // bit1 == 2, mask1 == 1  (iota == 1)_, _                                  //                        (iota == 2, unused)bit3, mask3                           // bit3 == 8, mask3 == 7  (iota == 3)
)

This last example exploits the implicit repetition【隱式的重復】 of the last non-empty expression list.

Type declarations

A type declaration binds an identifier, the type name, to a type. Type declarations come in two forms: alias declarations and type definitions.

TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
TypeSpec = AliasDecl | TypeDef .
Alias declarations

An alias declaration binds an identifier to the given type [Go 1.9].

AliasDecl = identifier [ TypeParameters ] "=" Type .

Within the scope of the identifier, it serves as an alias for the given type.

type (nodeList = []*Node  // nodeList and []*Node are identical typesPolar    = polar    // Polar and polar denote identical types
)

If the alias declaration specifies type parameters [Go 1.24], the type name denotes a generic alias【泛型別名】. Generic aliases must be instantiated when they are used.

type set[P comparable] = map[P]bool

In an alias declaration the given type cannot be a type parameter.

type A[P any] = P    // illegal: P is a type parameter
Type definitions

A type definition creates a new, distinct type with the same underlying type and operations as the given type and binds an identifier, the type name, to it.

TypeDef = identifier [ TypeParameters ] Type .

The new type is called a defined type. It is different from any other type, including the type it is created from.

type (Point struct{ x, y float64 }  // Point and struct{ x, y float64 } are different typespolar Point                   // polar and Point denote different types
)type TreeNode struct {left, right *TreeNodevalue any
}type Block interface {BlockSize() intEncrypt(src, dst []byte)Decrypt(src, dst []byte)
}

A defined type may have methods associated with it. It does not inherit any methods bound to the given type, but the method set of an interface type or of elements of a composite type remains unchanged:

重要!

// A Mutex is a data type with two methods, Lock and Unlock.
type Mutex struct         { /* Mutex fields */ }
func (m *Mutex) Lock()    { /* Lock implementation */ }
func (m *Mutex) Unlock()  { /* Unlock implementation */ }// NewMutex has the same composition as Mutex but its method set is empty. 
type NewMutex Mutex// The method set of PtrMutex's underlying type *Mutex remains unchanged,
// but the method set of PtrMutex is empty.
type PtrMutex *Mutex// The method set of *PrintableMutex contains the methods
// Lock and Unlock bound to its embedded field Mutex.
type PrintableMutex struct {Mutex
}// MyBlock is an interface type that has the same method set as Block.
type MyBlock Block

Type definitions may be used to define different boolean, numeric, or string types and associate methods with them:

type TimeZone intconst (EST TimeZone = -(5 + iota)CSTMSTPST
)func (tz TimeZone) String() string {return fmt.Sprintf("GMT%+dh", tz)
}

If the type definition specifies type parameters, the type name denotes a generic type. Generic types must be instantiated when they are used.

// 泛型類型
type List[T any] struct {next  *List[T]value T
}

In a type definition the given type cannot be a type parameter.

type T[P any] P    // illegal: P is a type parameterfunc f[T any]() {type L T   // illegal: T is a type parameter declared by the enclosing function
}

A generic type may also have methods associated with it. In this case, the method receivers must declare the same number of type parameters as present in the generic type definition.

// The method Len returns the number of elements in the linked list l.
func (l *List[T]) Len() int  {}

Type parameter declarations

A type parameter list declares the type parameters of a generic function or type declaration. The type parameter list looks like an ordinary function parameter list except that the type parameter names must all be present and the list is enclosed in square brackets rather than parentheses [Go 1.18].

TypeParameters = "[" TypeParamList [ "," ] "]" .
TypeParamList  = TypeParamDecl { "," TypeParamDecl } .
TypeParamDecl  = IdentifierList TypeConstraint .

All non-blank names in the list must be unique. Each name declares a type parameter, which is a new and different named type that acts as a placeholder for an (as of yet) unknown type in the declaration. The type parameter is replaced with a type argument upon instantiation of the generic function or type.

[P any]
[S interface{ ~[]byte|string }]
[S ~[]E, E any]
[P Constraint[int]]
[_ any]

Just as each ordinary function parameter has a parameter type, each type parameter has a corresponding (meta-)type which is called its type constraint.

A parsing ambiguity arises when the type parameter list for a generic type declares a single type parameter P with a constraint C such that the text P C forms a valid expression:

type T[P *C]type T[P (C)]type T[P *C|Q] …
…

上面的類型聲明會被解析為一個數組類型的聲明,有歧義。

In these rare cases, the type parameter list is indistinguishable from an expression and the type declaration is parsed as an array type declaration. To resolve the ambiguity, embed the constraint in an interface or use a trailing comma:

type T[P interface{*C}]type T[P *C,]

Type parameters may also be declared by the receiver specification of a method declaration associated with a generic type.

Within a type parameter list of a generic type T, a type constraint may not (directly, or indirectly through the type parameter list of another generic type) refer to T.

type T1[P T1[P]]// illegal: T1 refers to itself
type T2[P interface{ T2[int] }]// illegal: T2 refers to itself
type T3[P interface{ m(T3[int])}]// illegal: T3 refers to itself
type T4[P T5[P]]// illegal: T4 refers to T5 and
type T5[P T4[P]]//          T5 refers to T4type T6[P int] struct{ f *T6[P] }     // ok: reference to T6 is not in type parameter list
Type constraints

A type constraint is an interface that defines the set of permissible type arguments for the respective type parameter and controls the operations supported by values of that type parameter [Go 1.18].

TypeConstraint = TypeElem .

If the constraint is an interface literal of the form interface{E} where E is an embedded type element (not a method), in a type parameter list the enclosing interface{ … } may be omitted for convenience:

[T []P]                      // = [T interface{[]P}]
[T ~int]                     // = [T interface{~int}]
[T int|string]               // = [T interface{int|string}]
type Constraint ~int         // illegal: ~int is not in a type parameter list

The predeclared interface type comparable denotes the set of all non-interface types that are strictly comparable [Go 1.18].

Even though interfaces that are not type parameters are comparable, they are not strictly comparable and therefore they do not implement comparable. However, they satisfy comparable.

【接口類型不是嚴格可比較的,所以接口類型沒有實現comparable】


? Interface types that are not type parameters are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.

A type is strictly comparable if it is comparable and not an interface type nor composed of interface types. Specifically:

  • ? Boolean, numeric, string, pointer, and channel types are strictly comparable.
  • ? Struct types are strictly comparable if all their field types are strictly comparable.
  • ? Array types are strictly comparable if their array element types are strictly comparable.
  • ? Type parameters are strictly comparable if all types in their type set are strictly comparable.


A type T implements an interface I if

  • ? T is not an interface and is an element of the type set of I; or

  • ? T is an interface and the type set of T is a subset of the type set of I.


int                          // implements comparable (int is strictly comparable)
[]byte                       // does not implement comparable (slices cannot be compared)
interface{}                  // does not implement comparable (see above)
interface{ ~int | ~string }  // type parameter only: implements comparable (int, string types are strictly comparable)
interface{ comparable }      // type parameter only: implements comparable (comparable implements itself)
interface{ ~int | ~[]byte }  // type parameter only: does not implement comparable (slices are not comparable)
interface{ ~struct{ any } }  // type parameter only: does not implement comparable (field any is not strictly comparable)

The comparable interface and interfaces that (directly or indirectly) embed comparable may only be used as type constraints. They cannot be the types of values or variables, or components of other, non-interface types.

Satisfying a type constraint

A type argument T satisfies a type constraint C if T is an element of the type set defined by C; in other words, if T implements C. As an exception, a strictly comparable type constraint may also be satisfied by a comparable (not necessarily strictly comparable) type argument [Go 1.20]. More precisely:

A type T satisfies a constraint C if

  • T implements C; or
  • C can be written in the form interface{ comparable; E }, where E is a basic interface and T is comparable and implements E.
type argument      type constraint                // constraint satisfactionint                interface{ ~int }              // satisfied: int implements interface{ ~int }
string             comparable                     // satisfied: string implements comparable (string is strictly comparable)
[]byte             comparable                     // not satisfied: slices are not comparable
any                interface{ comparable; int }   // not satisfied: any does not implement interface{ int }
any                comparable                     // satisfied: any is comparable and implements the basic interface any
struct{f any}      comparable                     // satisfied: struct{f any} is comparable and implements the basic interface any
any                interface{ comparable; m() }   // not satisfied: any does not implement the basic interface interface{ m() }
interface{ m() }   interface{ comparable; m() }   // satisfied: interface{ m() } is comparable and implements the basic interface interface{ m() }

為什么 any 滿足 comparable?

根據規則的第二條,C 可以寫成 interface{ comparable; E },其中 E 是一個基本接口。對于 comparable,它可以看作 interface{ comparable; any },因為 any 是一個基本接口。any 是 comparable 的,并且實現了 any,因此 any 滿足 comparable 約束。

為什么 comparable 可以寫成 interface{ comparable; any }?
按照接口類型的類型集定義: interface{ comparable; any } 的類型集是 comparable 和 any 類型集的交集,由于any類型集是所有的非接口類型,所以它們的交集就是 comparable 的類型集。由于 interface{ comparable; any } 和 comparable 的類型集相同,它們是相同的類型【按照接口類型是否相同的定義】!


Because of the exception in the constraint satisfaction rule, comparing operands of type parameter type may panic at run-time (even though comparable type parameters are always strictly comparable).

Variable declarations

A variable declaration creates one or more variables, binds corresponding identifiers to them, and gives each a type and an initial value.

VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
var i int
var U, V, W float64
var k = 0
var x, y float32 = -1, -2
var (i       intu, v, s = 2.0, 3.0, "bar"
)
var re, im = complexSqrt(-1)
var _, found = entries[name]  // map lookup; only interested in "found"

If a list of expressions is given, the variables are initialized with the expressions following the rules for assignment statements. Otherwise, each variable is initialized to its zero value.

If a type is present, each variable is given that type. Otherwise, each variable is given the type of the corresponding initialization value in the assignment. If that value is an untyped constant, it is first implicitly converted to its default type; if it is an untyped boolean value, it is first implicitly converted to type bool. The predeclared identifier nil cannot be used to initialize a variable with no explicit type.

var d = math.Sin(0.5)  // d is float64
var i = 42             // i is int
var t, ok = x.(T)      // t is T, ok is bool
var n = nil            // illegal

Implementation restriction: A compiler may make it illegal to declare a variable inside a function body if the variable is never used.

Short variable declarations

A short variable declaration uses the syntax:

ShortVarDecl = IdentifierList ":=" ExpressionList .

It is shorthand for a regular variable declaration with initializer expressions but no types:

"var" IdentifierList "=" ExpressionList .
i, j := 0, 10
f := func() int { return 7 }
ch := make(chan int)
r, w, _ := os.Pipe()  // os.Pipe() returns a connected pair of Files and an error, if any
_, y, _ := coord(p)   // coord() returns three values; only interested in y coordinate

Unlike regular variable declarations, a short variable declaration may redeclare variables provided they were originally declared earlier in the same block (or the parameter lists if the block is the function body) with the same type, and at least one of the non-blank variables is new. As a consequence, redeclaration can only appear in a multi-variable short declaration. Redeclaration does not introduce a new variable; it just assigns a new value to the original. The non-blank variable names on the left side of := must be unique.

field1, offset := nextField(str, 0)
field2, offset := nextField(str, offset)  // redeclares offset
x, y, x := 1, 2, 3                        // illegal: x repeated on left side of :=

Short variable declarations may appear only inside functions. In some contexts such as the initializers for “if”, “for”, or “switch” statements, they can be used to declare local temporary variables.

Function declarations

A function declaration binds an identifier, the function name, to a function.

FunctionDecl = "func" FunctionName [ TypeParameters ] Signature [ FunctionBody ] .
FunctionName = identifier .
FunctionBody = Block .

If the function’s signature declares result parameters, the function body’s statement list must end in a terminating statement.

func IndexRune(s string, r rune) int {for i, c := range s {if c == r {return i}}// invalid: missing return statement
}

If the function declaration specifies type parameters, the function name denotes a generic function. A generic function must be instantiated before it can be called or used as a value.

func min[T ~int|~float64](x, y T) T {if x < y {return x}return y
}

A function declaration without type parameters may omit the body. Such a declaration provides the signature for a function implemented outside Go, such as an assembly routine.

func flushICache(begin, end uintptr)  // implemented externally

Method declarations

A method is a function with a receiver. A method declaration binds an identifier, the method name, to a method, and associates the method with the receiver’s base type.

MethodDecl = "func" Receiver MethodName Signature [ FunctionBody ] .
Receiver   = Parameters .

The receiver is specified via an extra parameter section preceding the method name. That parameter section must declare a single non-variadic parameter, the receiver. Its type must be a defined type T or a pointer to a defined type T, possibly followed by a list of type parameter names [P1, P2, …] enclosed in square brackets. T is called the receiver base type. A receiver base type cannot be a pointer or interface type and it must be defined in the same package as the method. The method is said to be bound to its receiver base type and the method name is visible only within selectors for type T or *T.

A non-blank receiver identifier must be unique in the method signature. If the receiver’s value is not referenced inside the body of the method, its identifier may be omitted in the declaration. The same applies in general to parameters of functions and methods.

For a base type, the non-blank names of methods bound to it must be unique. If the base type is a struct type, the non-blank method and field names must be distinct.

Given defined type Point the declarations

func (p *Point) Length() float64 {return math.Sqrt(p.x * p.x + p.y * p.y)
}func (p *Point) Scale(factor float64) {p.x *= factorp.y *= factor
}

bind the methods Length and Scale, with receiver type *Point, to the base type Point.

If the receiver base type is a generic type, the receiver specification must declare corresponding type parameters for the method to use. This makes the receiver type parameters available to the method. Syntactically, this type parameter declaration looks like an instantiation of the receiver base type: the type arguments must be identifiers denoting the type parameters being declared, one for each type parameter of the receiver base type. The type parameter names do not need to match their corresponding parameter names in the receiver base type definition, and all non-blank parameter names must be unique in the receiver parameter section and the method signature. The receiver type parameter constraints are implied by the receiver base type definition: corresponding type parameters have corresponding constraints.

type Pair[A, B any] struct {a Ab B
}func (p Pair[A, B]) Swap() Pair[B, A]  {}  // receiver declares A, B
func (p Pair[First, _]) First() First  {}  // receiver declares First, corresponds to A in Pair

If the receiver type is denoted by (a pointer to) an alias, the alias must not be generic and it must not denote an instantiated generic type, neither directly nor indirectly via another alias, and irrespective of pointer indirections.

type GPoint[P any] = Point
type HPoint        = *GPoint[int]
type IPair         = Pair[int, int]func (*GPoint[P]) Draw(P)   {}  // illegal: alias must not be generic
func (HPoint) Draw(P)       {}  // illegal: alias must not denote instantiated type GPoint[int]
func (*IPair) Second() int  {}  // illegal: alias must not denote instantiated type Pair[int, int]

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

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

相關文章

在 Ubuntu 上安裝 Docker 的完整指南

1. 卸載舊版本(如有) 在安裝新版本前,建議先卸載舊版本: sudo apt remove docker docker-engine docker.io containerd runc 2. 安裝依賴包 更新軟件包索引并安裝必要的依賴: sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release 3. 添加 Do…

turtle的九個使用

一 import turtle as t color [red,green,blue,orange,pink] for i in range(len(color)):t.penup()t.goto(-20070*i,0)t.pendown()t.pencolor(color[i])t.circle(50, steps 5) t.done()二 #在____________上補充代碼 #不要修改其他代碼import random as r import turtle a…

23種設計模式-備忘錄(Memento)設計模式

備忘錄設計模式 &#x1f6a9;什么是備忘錄設計模式&#xff1f;&#x1f6a9;備忘錄設計模式的特點&#x1f6a9;備忘錄設計模式的結構&#x1f6a9;備忘錄設計模式的優缺點&#x1f6a9;備忘錄設計模式的Java實現&#x1f6a9;代碼總結&#x1f6a9;總結 &#x1f6a9;什么是…

利用虛擬化技術實現高級Hook

虛擬化技術為系統監控和Hook提供了更強大、更隱蔽的實現方式。以下是幾種基于虛擬化的Hook技術實現方法&#xff1a; 1. 基于VT-x/AMD-V的硬件虛擬化Hook 基本原理 利用CPU的硬件虛擬化擴展(Intel VT-x/AMD-V)在Ring -1層級監控系統行為&#xff0c;實現無法被常規方法檢測的…

某魚、某寶 sign 簽名算法分析記錄

【作者主頁】&#xff1a;小魚神1024 【知識星球】&#xff1a;小魚神的逆向編程圈 【擅長領域】&#xff1a;JS逆向、小程序逆向、AST還原、驗證碼突防、Python開發、瀏覽器插件開發、React前端開發、NestJS后端開發等等 本文章中所有內容僅供學習交流使用&#xff0c;不用于其…

Compose筆記(十三)--事件總線

這一節了解一下Compose中的事件總線&#xff0c;在Jetpack Compose里&#xff0c;官方沒有直接提供事件總線&#xff0c;但可以借助第三方庫或者自定義實現來達成事件總線的功能&#xff0c;進而在不同的 Compose 控件間同步數據。 自定義事件總線 import androidx.compose.r…

Python的inspect模塊

在Python編程中&#xff0c;**inspect**模塊是一個強大的工具包&#xff0c;它提供了一系列函數來獲取對象的信息&#xff0c;主要用于獲取對象的源代碼、參數信息、類繼承關系、方法屬性等。這對于調試、自動化文檔生成、代碼分析等場景都非常有用。本文將詳細介紹inspect模塊…

2025跳槽學習計劃

&#xff08;1&#xff09;編程基礎&#xff1a; 目錄學習資料Chttps://www.bilibili.com/video/BV1z64y1U7hs?spm_id_from333.1387.favlist.content.clickLinuxPytorchhttps://www.bilibili.com/video/BV1if4y147hS?spm_id_from333.1387.favlist.content.clickopencv數據結…

WebRTC簡介及應用

WebRTC&#xff08;Web Real-Time Communication&#xff09;是一種支持瀏覽器和移動設備進行實時音視頻通信的技術&#xff0c;無需安裝插件或額外的軟件。它是一個開放標準&#xff0c;最初由Google推動&#xff0c;并被W3C&#xff08;萬維網聯盟&#xff09;和IETF&#xf…

【C語言】分支與循環(上)

前言&#xff1a;C語言是由順序結構、選擇結構、循環結構組成的結構化的程序設計語言。 那C語言是如何設計和實現這些結構的呢&#xff1f;話不多說&#xff0c;馬上開始。 三種結構如圖所示&#xff1a; 我們可以使用 if else語句、 switch語句 來實現選擇&#xff08;分支&am…

一次與chatgpt關于VO的深入討論。

我&#xff1a; {"usageRate":50,"projectInfo":[{"productName":"長江一號","deviceInfo":[{"deviceName":"AA","num":10},{"deviceName":"BB","num":3}]},…

Springboot學習筆記3.20

目錄 1.實戰篇第一課 我們將會在本次實戰中學習到哪些知識點&#xff1f; 開發模式和環境搭建&#xff1a; 注冊接口 1.Lombok 2.開發流程 1.controller層&#xff0c;這個層會指明訪問路徑和要執行的邏輯&#xff1a; 2.我們把返回結果根據接口文檔包裝成一個類result&a…

docker save如何遷移鏡像更節省空間?

文章目錄 方法一&#xff1a;使用docker save命令方法二&#xff1a;直接保存多個鏡像到一個tar文件哪個方法更節省磁盤空間&#xff1f;空間效率對比實際測試示例其他優勢結論 如何用腳本遷移加載鏡像 遷移鏡像時候&#xff0c;往往會碰到基礎鏡像相同的很多鏡像需要遷移&…

全新升級 | Built For You Spring ‘25 發布,Fin 智能客服實現新突破!

圖像識別、語音交互、任務自動化&#xff0c;立即體驗智能客服蛻變&#xff01; 上周&#xff0c;Intercom 舉辦了 Built For You Spring 25 發布會&#xff0c;正式揭曉了 AI Agent Fin 的一系列令人振奮的更新。Fin 正在以前所未有的速度革新客戶支持模式——它已經成功解決了…

需求導向的K8S網絡原理分析:Kube-proxy、Flannel、Calico的地位和作用

最近發現自己似乎從來沒學明白過Kubernetes網絡通信方案&#xff0c;特開一貼復習總結一下。 在k8s中&#xff0c;每個 Pod 都擁有一個獨立的 IP 地址&#xff0c;而且假定所有 Pod 都在一個可以直接連通的、扁平的網絡空間中。所以不管它們是否允許在同一個 Node&#xff08;宿…

Vulnhub:Digitalword.local: FALL靶機滲透

將靶機按照圖中連接方式打開&#xff0c;fall在virtualBox中打開 信息收集 掃描得ip arp-scan -l 掃描端口 nmap -A -T4 -sV -p- 掃描目錄 gobuster dir -u http://192.168.117.160 -x php,txt,html -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt 一個一個…

4、網工軟考—VLAN配置—hybird配置

1、實驗環境搭建&#xff1a; 2、實驗過程 SW1&#xff1a; 先創建vlan2和vlan3 [Huawei-Ethernet0/0/2]port link-type hybrid //hybird端口 [Huawei-Ethernet0/0/2]port hybrid pvid vlan 2 [Huawei-Ethernet0/0/2]port hybrid untagged vlan 10 //撕掉vlan10的標簽 …

【OCR】技術

OCR圖像識別 一、OCR是什么二、Python中如何實現OCR1.簡單應用 三、OCR的核心步驟1.?圖像預處理?&#xff08;提高識別準確率&#xff09;2.?文字識別3.?輸出結果 四、OCR到的應用場景五、注意事項六、擴展學習 此貼用來更新在工作中遇到的一些圖片解析內容 一、OCR是什么 …

深入理解 dispatchEvent:前端事件觸發的藝術

dispatchEvent 是 DOM 元素的一個方法&#xff0c;用于手動觸發/派發一個事件。這個方法允許開發者以編程方式觸發事件&#xff0c;而不是等待用戶交互或瀏覽器自動觸發。 1.基本概念 作用&#xff1a;dispatchEvent 用于在指定的 DOM 節點上觸發一個事件 使用場景&#xff1…

2025年數智化電商產業帶發展研究報告260+份匯總解讀|附PDF下載

原文鏈接&#xff1a;https://tecdat.cn/?p41286 在數字技術與實體經濟深度融合的當下&#xff0c;數智化產業帶正成為經濟發展的關鍵引擎。 從云南鮮花產業帶的直播熱銷到深圳3C數碼的智能轉型&#xff0c;數智化正重塑產業格局。2023年數字經濟規模突破53.9萬億元&#xff…