Go 语言规范学习(4)

文章目录

    • Expressions
      • Operands【操作数】
      • Qualified identifiers
      • Composite literals
      • Function literals
      • Primary expressions
      • Selectors【选择器】
      • Method expressions
      • Method values
      • Index expressions
      • Slice expressions
        • Simple slice expressions
        • Full slice expressions
      • Type assertions
      • Calls
      • Passing arguments to `...` parameters
      • Instantiations
      • Type inference
        • Type unification

Expressions

An expression specifies the computation of a value by applying operators and functions to operands.

Operands【操作数】

Operands denote the elementary values in an expression. An operand may be a literal, a (possibly qualified) non-blank identifier denoting a constant, variable, or function, or a parenthesized expression.

Operand     = Literal | OperandName [ TypeArgs ] | "(" Expression ")" .
Literal     = BasicLit | CompositeLit | FunctionLit .
BasicLit    = int_lit | float_lit | imaginary_lit | rune_lit | string_lit .
OperandName = identifier | QualifiedIdent .

An operand name denoting a generic function may be followed by a list of type arguments; the resulting operand is an instantiated function.

The blank identifier may appear as an operand only on the left-hand side of an assignment statement.

Implementation restriction: A compiler need not report an error if an operand’s type is a type parameter with an empty type set. Functions with such type parameters cannot be instantiated; any attempt will lead to an error at the instantiation site.

Qualified identifiers

A qualified identifier is an identifier qualified with a package name prefix. Both the package name and the identifier must not be blank.

QualifiedIdent = PackageName "." identifier .

A qualified identifier accesses an identifier in a different package, which must be imported. The identifier must be exported and declared in the package block of that package.

math.Sin // denotes the Sin function in package math

Composite literals

Composite literals construct new composite values each time they are evaluated. They consist of the type of the literal followed by a brace-bound list of elements. Each element may optionally be preceded by a corresponding key.

CompositeLit = LiteralType LiteralValue .
LiteralType  = StructType | ArrayType | "[" "..." "]" ElementType |SliceType | MapType | TypeName [ TypeArgs ] .
LiteralValue = "{" [ ElementList [ "," ] ] "}" .
ElementList  = KeyedElement { "," KeyedElement } .
KeyedElement = [ Key ":" ] Element .
Key          = FieldName | Expression | LiteralValue .
FieldName    = identifier .
Element      = Expression | LiteralValue .

The LiteralType’s core type T must be a struct, array, slice, or map type (the syntax enforces this constraint except when the type is given as a TypeName). The types of the elements and keys must be assignable to the respective field, element, and key types of type T; there is no additional conversion. The key is interpreted as a field name for struct literals, an index for array and slice literals, and a key for map literals. For map literals, all elements must have a key. It is an error to specify multiple elements with the same field name or constant key value. For non-constant map keys, see the section on evaluation order.

For struct literals the following rules apply:

  • A key must be a field name declared in the struct type.
  • An element list that does not contain any keys must list an element for each struct field in the order in which the fields are declared.
  • If any element has a key, every element must have a key.
  • An element list that contains keys does not need to have an element for each struct field. Omitted fields get the zero value for that field.
  • A literal may omit the element list; such a literal evaluates to the zero value for its type.
  • It is an error to specify an element for a non-exported field of a struct belonging to a different package.

Given the declarations

type Point3D struct { x, y, z float64 }
type Line struct { p, q Point3D }

one may write

origin := Point3D{}                            // zero value for Point3D
line := Line{origin, Point3D{y: -4, z: 12.3}}  // zero value for line.q.x

For array and slice literals the following rules apply:

  • Each element has an associated integer index marking its position in the array.
  • An element with a key uses the key as its index. The key must be a non-negative constant representable by a value of type int; and if it is typed it must be of integer type.
  • An element without a key uses the previous element’s index plus one. If the first element has no key, its index is zero.

Taking the address of a composite literal generates a pointer to a unique variable initialized with the literal’s value.

var pointer *Point3D = &Point3D{y: 1000}

Note that the zero value for a slice or map type is not the same as an initialized but empty value of the same type. Consequently, taking the address of an empty slice or map composite literal does not have the same effect as allocating a new slice or map value with new.

p1 := &[]int{}    // p1 points to an initialized, empty slice with value []int{} and length 0。已经初始化
p2 := new([]int)  // p2 points to an uninitialized slice with value nil and length 0。未初始化

The length of an array literal is the length specified in the literal type. If fewer elements than the length are provided in the literal, the missing elements are set to the zero value for the array element type. It is an error to provide elements with index values outside the index range of the array. The notation ... specifies an array length equal to the maximum element index plus one.

buffer := [10]string{}             // len(buffer) == 10
intSet := [6]int{1, 2, 3, 5}       // len(intSet) == 6
days := [...]string{"Sat", "Sun"}  // len(days) == 2

A slice literal describes the entire underlying array literal. Thus the length and capacity of a slice literal are the maximum element index plus one. A slice literal has the form

[]T{x1, x2, … xn}

and is shorthand for a slice operation applied to an array:

tmp := [n]T{x1, x2, … xn}
tmp[0 : n]

Within a composite literal of array, slice, or map type T, elements or map keys that are themselves composite literals may elide the respective literal type if it is identical to the element or key type of T. Similarly, elements or keys that are addresses of composite literals may elide the &T when the element or key type is *T.

[...]Point{{1.5, -3.5}, {0, 0}}     // same as [...]Point{Point{1.5, -3.5}, Point{0, 0}}
[][]int{{1, 2, 3}, {4, 5}}          // same as [][]int{[]int{1, 2, 3}, []int{4, 5}}
[][]Point{{{0, 1}, {1, 2}}}         // same as [][]Point{[]Point{Point{0, 1}, Point{1, 2}}}
map[string]Point{"orig": {0, 0}}    // same as map[string]Point{"orig": Point{0, 0}}
map[Point]string{{0, 0}: "orig"}    // same as map[Point]string{Point{0, 0}: "orig"}type PPoint *Point
[2]*Point{{1.5, -3.5}, {}}          // same as [2]*Point{&Point{1.5, -3.5}, &Point{}}
[2]PPoint{{1.5, -3.5}, {}}          // same as [2]PPoint{PPoint(&Point{1.5, -3.5}), PPoint(&Point{})}

A parsing ambiguity arises when a composite literal using the TypeName form of the LiteralType appears as an operand between the keyword and the opening brace of the block of an “if”, “for”, or “switch” statement, and the composite literal is not enclosed in parentheses, square brackets, or curly braces. In this rare case, the opening brace of the literal is erroneously parsed as the one introducing the block of statements. To resolve the ambiguity, the composite literal must appear within parentheses.

if x == (T{a,b,c}[i]) {}
if (x == T{a,b,c}[i]) {}

Examples of valid array, slice, and map literals:

// list of prime numbers
primes := []int{2, 3, 5, 7, 9, 2147483647}// vowels[ch] is true if ch is a vowel
vowels := [128]bool{'a': true, 'e': true, 'i': true, 'o': true, 'u': true, 'y': true}// the array [10]float32{-1, 0, 0, 0, -0.1, -0.1, 0, 0, 0, -1}
filter := [10]float32{-1, 4: -0.1, -0.1, 9: -1}// frequencies in Hz for equal-tempered scale (A4 = 440Hz)
noteFrequency := map[string]float32{"C0": 16.35, "D0": 18.35, "E0": 20.60, "F0": 21.83,"G0": 24.50, "A0": 27.50, "B0": 30.87,
}

Function literals

A function literal represents an anonymous function. Function literals cannot declare type parameters.

FunctionLit = "func" Signature FunctionBody .
func(a, b int, z float64) bool { return a*b < int(z) }

A function literal can be assigned to a variable or invoked directly.

f := func(x, y int) int { return x + y }
func(ch chan int) { ch <- ACK }(replyChan)

Function literals are closures: they may refer to variables defined in a surrounding function. Those variables are then shared between the surrounding function and the function literal, and they survive as long as they are accessible.

Primary expressions

Primary expressions are the operands for unary and binary expressions.

PrimaryExpr   = Operand |Conversion |MethodExpr |PrimaryExpr Selector |PrimaryExpr Index |PrimaryExpr Slice |PrimaryExpr TypeAssertion |PrimaryExpr Arguments .Selector      = "." identifier .
Index         = "[" Expression [ "," ] "]" .
Slice         = "[" [ Expression ] ":" [ Expression ] "]" |"[" [ Expression ] ":" Expression ":" Expression "]" .
TypeAssertion = "." "(" Type ")" .
Arguments     = "(" [ ( ExpressionList | Type [ "," ExpressionList ] ) [ "..." ] [ "," ] ] ")" .
x
2
(s + ".txt")
f(3.1415, true)
Point{1, 2}
m["foo"]
s[i : j + 1]
obj.color
f.p[i].x()

Selectors【选择器】

For a primary expression x that is not a package name, the selector expression

x.f

denotes the field or method f of the value x (or sometimes *x; see below). The identifier f is called the (field or method) selector; it must not be the blank identifier. The type of the selector expression is the type of f. If x is a package name, see the section on qualified identifiers.

A selector f may denote a field or method f of a type T, or it may refer to a field or method f of a nested embedded field of T. The number of embedded fields traversed to reach f is called its depth in T. The depth of a field or method f declared in T is zero. The depth of a field or method f declared in an embedded field A in T is the depth of f in A plus one.

The following rules apply to selectors:

  1. For a value x of type T or *T where T is not a pointer or interface type, x.f denotes the field or method at the shallowest depth in T where there is such an f. If there is not exactly one f with shallowest depth, the selector expression is illegal.
  2. For a value x of type I where I is an interface type, x.f denotes the actual method with name f of the dynamic value of x. If there is no method with name f in the method set of I, the selector expression is illegal.
  3. As an exception, if the type of x is a defined pointer type and (*x).f is a valid selector expression denoting a field (but not a method), x.f is shorthand for (*x).f.
  4. In all other cases, x.f is illegal.
  5. If x is of pointer type and has the value nil and x.f denotes a struct field, assigning to or evaluating x.f causes a run-time panic.
  6. If x is of interface type and has the value nil, calling or evaluating the method x.f causes a run-time panic.

For example, given the declarations:

type T0 struct {x int
}func (*T0) M0()type T1 struct {y int
}func (T1) M1()type T2 struct {z intT1*T0
}func (*T2) M2()type Q *T2var t T2     // with t.T0 != nil
var p *T2    // with p != nil and (*p).T0 != nil
var q Q = p

one may write:

t.z          // t.z
t.y          // t.T1.y
t.x          // (*t.T0).xp.z          // (*p).z
p.y          // (*p).T1.y
p.x          // (*(*p).T0).xq.x          // (*(*q).T0).x        (*q).x is a valid field selectorp.M0()       // ((*p).T0).M0()      M0 expects *T0 receiver
p.M1()       // ((*p).T1).M1()      M1 expects T1 receiver
p.M2()       // p.M2()              M2 expects *T2 receiver
t.M2()       // (&t).M2()           M2 expects *T2 receiver, see section on Calls

but the following is invalid:

q.M0()       // (*q).M0 is valid but not a field selector

Method expressions

If M is in the method set of type T, T.M is a function that is callable as a regular function with the same arguments as M prefixed by an additional argument that is the receiver of the method.

MethodExpr   = ReceiverType "." MethodName .
ReceiverType = Type .

Consider a struct type T with two methods, Mv, whose receiver is of type T, and Mp, whose receiver is of type *T.

type T struct {a int
}
func (tv  T) Mv(a int) int         { return 0 }  // value receiver
func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receivervar t T

The expression

T.Mv

yields a function equivalent to Mv but with an explicit receiver as its first argument; it has signature

func(tv T, a int) int

That function may be called normally with an explicit receiver, so these five invocations are equivalent:

t.Mv(7)
T.Mv(t, 7)
(T).Mv(t, 7)
f1 := T.Mv; f1(t, 7)
f2 := (T).Mv; f2(t, 7)

Similarly, the expression

(*T).Mp

yields a function value representing Mp with signature

func(tp *T, f float32) float32

For a method with a value receiver, one can derive a function with an explicit pointer receiver, so

(*T).Mv

yields a function value representing Mv with signature

func(tv *T, a int) int

Such a function indirects through the receiver to create a value to pass as the receiver to the underlying method; the method does not overwrite the value whose address is passed in the function call.

The final case, a value-receiver function for a pointer-receiver method, is illegal because pointer-receiver methods are not in the method set of the value type.【T.Mp是不合法的,因为类型T的方法集中不包含接收者是*T类型的方法Mp】

Function values derived from methods are called with function call syntax; the receiver is provided as the first argument to the call. That is, given f := T.Mv, f is invoked as f(t, 7) not t.f(7). To construct a function that binds the receiver, use a function literal or method value.

It is legal to derive a function value from a method of an interface type. The resulting function takes an explicit receiver of that interface type.

Method values

If the expression x has static type T and M is in the method set of type T, x.M is called a method value. The method value x.M is a function value that is callable with the same arguments as a method call of x.M. The expression x is evaluated and saved during the evaluation of the method value; the saved copy is then used as the receiver in any calls, which may be executed later.

type S struct { *T }
type T int
func (t T) M() { print(t) }t := new(T)
s := S{T: t}
f := t.M                    // receiver *t is evaluated and stored in f
g := s.M                    // receiver *(s.T) is evaluated and stored in g
*t = 42                     // does not affect stored receivers in f and g

The type T may be an interface or non-interface type.

As in the discussion of method expressions above, consider a struct type T with two methods, Mv, whose receiver is of type T, and Mp, whose receiver is of type *T.

type T struct {a int
}
func (tv  T) Mv(a int) int         { return 0 }  // value receiver
func (tp *T) Mp(f float32) float32 { return 1 }  // pointer receivervar t T
var pt *T
func makeT() T

The expression

t.Mv

yields a function value of type

func(int) int

These two invocations are equivalent:

t.Mv(7)
f := t.Mv; f(7)

Similarly, the expression

pt.Mp

yields a function value of type

func(float32) float32

As with selectors, a reference to a non-interface method with a value receiver using a pointer will automatically dereference that pointer: pt.Mv is equivalent to (*pt).Mv.

As with method calls, a reference to a non-interface method with a pointer receiver using an addressable value will automatically take the address of that value: t.Mp is equivalent to (&t).Mp.【?这不是因为选择器吗】

f := t.Mv; f(7)   // like t.Mv(7)
f := pt.Mp; f(7)  // like pt.Mp(7)
f := pt.Mv; f(7)  // like (*pt).Mv(7)
f := t.Mp; f(7)   // like (&t).Mp(7)
f := makeT().Mp   // invalid: result of makeT() is not addressable

Although the examples above use non-interface types, it is also legal to create a method value from a value of interface type.

var i interface { M(int) } = myVal
f := i.M; f(7)  // like i.M(7)

Index expressions

A primary expression of the form

a[x]

denotes the element of the array, pointer to array, slice, string or map a indexed by x. The value x is called the index or map key, respectively. The following rules apply:

If a is neither a map nor a type parameter:

  • the index x must be an untyped constant or its core type must be an integer
  • a constant index must be non-negative and representable by a value of type int
  • a constant index that is untyped is given type int
  • the index x is in range if 0 <= x < len(a), otherwise it is out of range

For a of array type A:

  • a constant index must be in range
  • if x is out of range at run time, a run-time panic occurs
  • a[x] is the array element at index x and the type of a[x] is the element type of A

For a of pointer to array type:

  • a[x] is shorthand for (*a)[x]

For a of slice type S:

  • if x is out of range at run time, a run-time panic occurs
  • a[x] is the slice element at index x and the type of a[x] is the element type of S

For a of string type:

  • a constant index must be in range if the string a is also constant
  • if x is out of range at run time, a run-time panic occurs
  • a[x] is the non-constant byte value at index x and the type of a[x] is byte
  • a[x] may not be assigned to

For a of map type M:

  • x’s type must be assignable to the key type of M
  • if the map contains an entry with key x, a[x] is the map element with key x and the type of a[x] is the element type of M
  • if the map is nil or does not contain such an entry, a[x] is the zero value for the element type of M

For a of type parameter type P:

  • The index expression a[x] must be valid for values of all types in P’s type set.
  • The element types of all types in P’s type set must be identical. In this context, the element type of a string type is byte.
  • If there is a map type in the type set of P, all types in that type set must be map types, and the respective key types must be all identical.
  • a[x] is the array, slice, or string element at index x, or the map element with key x of the type argument that P is instantiated with, and the type of a[x] is the type of the (identical) element types.
  • a[x] may not be assigned to if P’s type set includes string types.

Otherwise a[x] is illegal.

An index expression on a map a of type map[K]V used in an assignment statement or initialization of the special form

v, ok = a[x]
v, ok := a[x]
var v, ok = a[x]

yields an additional untyped boolean value. The value of ok is true if the key x is present in the map, and false otherwise.

Assigning to an element of a nil map causes a run-time panic.

Slice expressions

Slice expressions construct a substring or slice from a string, array, pointer to array, or slice. There are two variants: a simple form that specifies a low and high bound, and a full form that also specifies a bound on the capacity.

Simple slice expressions

The primary expression

a[low : high]

constructs a substring or slice. The core type of a must be a string, array, pointer to array, slice, or a bytestring. The indices low and high select which elements of operand a appear in the result. The result has indices starting at 0 and length equal to high - low. After slicing the array a

a := [5]int{1, 2, 3, 4, 5}
s := a[1:4]

the slice s has type []int, length 3, capacity 4, and elements

s[0] == 2
s[1] == 3
s[2] == 4

For convenience, any of the indices may be omitted. A missing low index defaults to zero; a missing high index defaults to the length of the sliced operand:

a[2:]  // same as a[2 : len(a)]
a[:3]  // same as a[0 : 3]
a[:]   // same as a[0 : len(a)]

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

For arrays or strings, the indices are in range if 0 <= low <= high <= len(a), otherwise they are out of range. For slices, the upper index bound is the slice capacity cap(a) rather than the length. A constant index must be non-negative and representable by a value of type int; for arrays or constant strings, constant indices must also be in range. If both indices are constant, they must satisfy low <= high. If the indices are out of range at run time, a run-time panic occurs.

Except for untyped strings, if the sliced operand is a string or slice, the result of the slice operation is a non-constant value of the same type as the operand. For untyped string operands the result is a non-constant value of type string. If the sliced operand is an array, it must be addressable and the result of the slice operation is a slice with the same element type as the array.

If the sliced operand of a valid slice expression is a nil slice, the result is a nil slice. Otherwise, if the result is a slice, it shares its underlying array with the operand.

var a [10]int
s1 := a[3:7]   // underlying array of s1 is array a; &s1[2] == &a[5]
s2 := s1[1:4]  // underlying array of s2 is underlying array of s1 which is array a; &s2[1] == &a[5]
s2[1] = 42     // s2[1] == s1[2] == a[5] == 42; they all refer to the same underlying array elementvar s []int
s3 := s[:0]    // s3 == nil
Full slice expressions

The primary expression

a[low : high : max]

constructs a slice of the same type, and with the same length and elements as the simple slice expression a[low : high]. Additionally, it controls the resulting slice’s capacity by setting it to max - low. Only the first index may be omitted; it defaults to 0. The core type of a must be an array, pointer to array, or slice (but not a string). After slicing the array a

a := [5]int{1, 2, 3, 4, 5}
t := a[1:3:5]

the slice t has type []int, length 2, capacity 4, and elements

t[0] == 2
t[1] == 3

As for simple slice expressions, if a is a pointer to an array, a[low : high : max] is shorthand for (*a)[low : high : max]. If the sliced operand is an array, it must be addressable.

The indices are in range if 0 <= low <= high <= max <= cap(a), otherwise they are out of range. A constant index must be non-negative and representable by a value of type int; for arrays, constant indices must also be in range. If multiple indices are constant, the constants that are present must be in range relative to each other. If the indices are out of range at run time, a run-time panic occurs.

Type assertions

For an expression x of interface type, but not a type parameter, and a type T, the primary expression

x.(T)

asserts that x is not nil and that the value stored in x is of type T. The notation x.(T) is called a type assertion.

More precisely, if T is not an interface type, x.(T) asserts that the dynamic type of x is identical to the type T【x的动态类型是否是类型T】. In this case, T must implement the (interface) type of x; otherwise the type assertion is invalid since it is not possible for x to store a value of type T.

If T is an interface type, x.(T) asserts that the dynamic type of x implements the interface T【x的动态类型是否实现了接口T】.

If the type assertion holds, the value of the expression is the value stored in x and its type is T. If the type assertion is false, a run-time panic occurs. In other words, even though the dynamic type of x is known only at run time, the type of x.(T) is known to be T in a correct program.

var x interface{} = 7          // x has dynamic type int and value 7
i := x.(int)                   // i has type int and value 7type I interface { m() }func f(y I) {s := y.(string)        // illegal: string does not implement I (missing method m)r := y.(io.Reader)     // r has type io.Reader and the dynamic type of y must implement both I and io.Reader}

A type assertion used in an assignment statement or initialization of the special form

v, ok = x.(T)
v, ok := x.(T)
var v, ok = x.(T)
var v, ok interface{} = x.(T) // dynamic types of v and ok are T and bool

yields an additional untyped boolean value. The value of ok is true if the assertion holds. Otherwise it is false and the value of v is the zero value for type T. No run-time panic occurs in this case.

Calls

Given an expression f with a core type F of function type,

f(a1, a2, … an)

calls f with arguments a1, a2, … an. Except for one special case, arguments must be single-valued expressions assignable to the parameter types of F and are evaluated before the function is called. The type of the expression is the result type of F. A method invocation is similar but the method itself is specified as a selector upon a value of the receiver type for the method.

math.Atan2(x, y)  // function call
var pt *Point
pt.Scale(3.5)     // method call with receiver pt

If f denotes a generic function, it must be instantiated before it can be called or used as a function value.

In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, new storage is allocated for the function’s variables, which includes its parameters and results. Then, the arguments of the call are passed to the function, which means that they are assigned to their corresponding function parameters, and the called function begins execution. The return parameters of the function are passed back to the caller when the function returns.

Calling a nil function value causes a run-time panic.

As a special case, if the return values of a function or method g are equal in number and individually assignable to the parameters of another function or method f, then the call f(g(*parameters_of_g*)) will invoke f after passing the return values of g to the parameters of f in order. The call of f must contain no parameters other than the call of g, and g must have at least one return value. If f has a final ... parameter, it is assigned the return values of g that remain after assignment of regular parameters.

func Split(s string, pos int) (string, string) {return s[0:pos], s[pos:]
}func Join(s, t string) string {return s + t
}if Join(Split(value, len(value)/2)) != value {log.Panic("test fails")
}

A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x’s method set contains m, x.m() is shorthand for (&x).m():

var p Point
p.Scale(3.5)

There is no distinct method type and there are no method literals.

Passing arguments to ... parameters

If f is variadic with a final parameter p of type ...T, then within f the type of p is equivalent to type []T. If f is invoked with no actual arguments for p, the value passed to p is nil. Otherwise, the value passed is a new slice of type []T with a new underlying array whose successive elements are the actual arguments, which all must be assignable to T. The length and capacity of the slice is therefore the number of arguments bound to p and may differ for each call site.

Given the function and calls

func Greeting(prefix string, who ...string)
Greeting("nobody")
Greeting("hello:", "Joe", "Anna", "Eileen")

within Greeting, who will have the value nil in the first call, and []string{"Joe", "Anna", "Eileen"} in the second.

If the final argument is assignable to a slice type []T and is followed by ..., it is passed unchanged as the value for a ...T parameter. In this case no new slice is created.

Given the slice s and call

s := []string{"James", "Jasmine"}
Greeting("goodbye:", s...)

within Greeting, who will have the same value as s with the same underlying array.

Instantiations

A generic function or type is instantiated by substituting type arguments for the type parameters [Go 1.18]. Instantiation proceeds in two steps:

  1. Each type argument is substituted for its corresponding type parameter in the generic declaration. This substitution happens across the entire function or type declaration, including the type parameter list itself and any types in that list.
  2. After substitution, each type argument must satisfy the constraint (instantiated, if necessary) of the corresponding type parameter. Otherwise instantiation fails.

Instantiating a type results in a new non-generic named type; instantiating a function produces a new non-generic function.

type parameter list    type arguments    after substitution[P any]                int               int satisfies any
[S ~[]E, E any]        []int, int        []int satisfies ~[]int, int satisfies any
[P io.Writer]          string            illegal: string doesn't satisfy io.Writer
[P comparable]         any               any satisfies (but does not implement) comparable

When using a generic function, type arguments may be provided explicitly, or they may be partially or completely inferred from the context in which the function is used. Provided that they can be inferred, type argument lists may be omitted entirely if the function is:

  • ​ called with ordinary arguments,
  • ​ assigned to a variable with a known type
  • ​ passed as an argument to another function, or
  • ​ returned as a result.

In all other cases, a (possibly partial) type argument list must be present. If a type argument list is absent or partial, all missing type arguments must be inferrable from the context in which the function is used.

// sum returns the sum (concatenation, for strings) of its arguments.
func sum[T ~int | ~float64 | ~string](x... T) T {}x := sum                       // illegal: the type of x is unknown
intSum := sum[int]             // intSum has type func(x... int) int
a := intSum(2, 3)              // a has value 5 of type int
b := sum[float64](2.0, 3)      // b has value 5.0 of type float64
c := sum(b, -1)                // c has value 4.0 of type float64type sumFunc func(x... string) string
var f sumFunc = sum            // same as var f sumFunc = sum[string]
f = sum                        // same as f = sum[string]

A partial type argument list cannot be empty; at least the first argument must be present. The list is a prefix of the full list of type arguments, leaving the remaining arguments to be inferred. Loosely speaking, type arguments may be omitted from “right to left”.

func apply[S ~[]E, E any](s S, f func(E) E) S {}f0 := apply[]                  // illegal: type argument list cannot be empty
f1 := apply[[]int]             // type argument for S explicitly provided, type argument for E inferred
f2 := apply[[]string, string]  // both type arguments explicitly providedvar bytes []byte
r := apply(bytes, func(byte) byte {})  // both type arguments inferred from the function arguments

For a generic type, all type arguments must always be provided explicitly.

Type inference

A use of a generic function may omit some or all type arguments if they can be inferred from the context within which the function is used, including the constraints of the function’s type parameters. Type inference succeeds if it can infer the missing type arguments and instantiation succeeds with the inferred type arguments. Otherwise, type inference fails and the program is invalid.

忽略,概念复杂

Type unification

忽略,概念复杂

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/42329.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript 性能优化实战:突破瓶颈,打造极致 Web 体验

在当今快节奏的互联网时代&#xff0c;用户对于 Web 应用的性能要求越来越高。一个响应迅速、流畅运行的 Web 页面能够极大地提升用户体验&#xff0c;反之&#xff0c;缓慢的加载速度和卡顿的交互则可能导致用户流失。JavaScript 作为 Web 开发的核心语言之一&#xff0c;其性…

《白帽子讲 Web 安全》之服务端请求伪造(SSRF)深度剖析:从攻击到防御

引言 在当今复杂的网络环境中&#xff0c;Web 应用安全犹如一座时刻需要精心守护的堡垒。随着技术的不断演进&#xff0c;各类安全威胁层出不穷&#xff0c;其中服务端请求伪造&#xff08;SSRF&#xff09;正逐渐成为令开发者与安全从业者头疼的一大难题。吴翰清在《白帽子讲…

Pandas的轴,axis=0,axis=1

八. Pandas的轴 axis0代表跨行&#xff08;down)&#xff0c;而axis1代表跨列&#xff08;across) 使用0值表示沿着每一列或行标签\索引值向下执行方法使用1值表示沿着每一行或者列标签模向执行对应的方法 下图代表在DataFrame当中axis为0和1时分别代表的含义: axis参数作用…

matplotlib学习

开始学习Python数据可视化 一.基础绘图函数 1.创建画布与坐标轴 import matplotlib.pyplot as plt# 创建画布和坐标轴 fig, ax plt.subplots() # 默认1行1列&#xff0c;返回Figure对象和Axes对象 2.绘制线图 x [1, 2, 3, 4] y [10, 20, 15, 25]# 绘制线图 ax.plot(x,…

系统架构设计前的多角度思考

首先&#xff0c;从需求分析入手&#xff0c;不仅关注当前功能&#xff0c;还要考虑业务未来的扩展方向。比如数据量预估增长多少&#xff1f;这些都是影响架构的重要因素。 然后是架构设计原则&#xff0c;比如分层设计、模块化、高内聚低耦合等。比如如何划分服务边界&#x…

leetcode230.二叉搜索树中第k小的元素

中序遍历&#xff0c;第k次出现的数值就是结果 /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left…

运筹说 第134期 | 矩阵对策的解法

上一期我们了解了矩阵对策的基本理论&#xff0c;包含矩阵对策的纯策略、矩阵对策的混合策略和矩阵对策的基本定理。 接下来小编将为大家介绍矩阵对策的解法&#xff0c;包括图解法、方程组法和线性规划法三种经典方法。 01 图解法 本节首先介绍矩阵对策的图解法&#xff0c;…

Python贝叶斯分层模型专题|对环境健康、医学心梗患者、体育赛事数据空间异质性实证分析合集|附数据代码

全文链接&#xff1a;https://tecdat.cn/?p41267 在大数据时代&#xff0c;多水平数据结构广泛存在于环境健康、医学研究和体育赛事等领域。本专题合集聚焦贝叶斯分层模型&#xff08;Hierarchical Bayesian Model&#xff09;的创新应用&#xff0c;通过氡气污染数据与 季后…

NOI2015提高组.子串

题目 520. 子串 思路 设计状态表示 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示 a a a的前 i i i个字符, b b b的前 j j j个字符, 并且已经分割了 k k k个子串的所有方案, 将状态划分为包含第 i i i个字符和不包含第 i i i个字符, 不包含第 i i i个字符的状态是 f [ i…

医疗智能体通信整合-大模型训练中沟通优化策略研究

一、引言:医疗模型训练的沟通困境 1.1 医疗 AI 发展背景 在数智化浪潮的推动下,医疗 AI 正以前所未有的速度融入现代医疗体系。从智能影像诊断助力医生精准识别病灶,到基于大数据分析的个性化药物研发,医疗 AI 在提升医疗效率、改善医疗质量方面展现出巨大潜力。据相关数据…

存储管理(一)

目录 一、存储管理的功能 1.地址映射&#xff08;地址重定位&#xff09; 2.主存分配和回收 3.存储保护 4.主存扩充&#xff08;虚拟存储&#xff09; 二、程序的装入与链接 程序的装入&#xff1a; 程序的链接 三、连续分配方式 单一连续分配 固定分区分配 动态分…

SpringBoot学习笔记3.27

目录 实战篇第二课 1.注册参数的校验&#xff1a; 学习过程中遇到的问题&#xff1a; 1.什么是正则表达式 2.怎么自定义异常&#xff1f; 1. 创建全局异常处理类 2. 定义响应对象 3. 使用 ExceptionHandler 4. 设置响应状态码 5. 返回统一响应 6. 测试全局异常处理 …

基于springboot+vue的游戏账号交易系统的设计与实现

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

小测验——合并多个网格文件调用相机参数进行适配

文章目录 一、前言1.1 对于rule1.2 对于ask、agent、edit1.3 对于没有notepad二、代码展示一、前言 1.1 对于rule 对于.cursorrules里面的文件内容,就是从提示词、项目简介、技术架构、目录结构、代码规范这几方面进行介绍 1.2 对于ask、agent、edit 切换模式在聊天框下方…

敏捷测试(Agile Testing)

敏捷测试&#xff08;Agile Testing&#xff09; 敏捷测试是在敏捷开发&#xff08;Agile Development&#xff09;环境下进行的软件测试方法&#xff0c;强调快速反馈、持续测试、团队协作&#xff0c;以确保软件质量贯穿整个开发周期。与传统瀑布模型不同&#xff0c;敏捷测…

FreeRTOS内核实现与应用学习之6——多优先级

在FreeRTOS中&#xff0c;数字优先级越小&#xff0c;逻辑优先级也越小&#xff1b;在任务创建时&#xff0c;会根据任务的优先级将任务插入就绪列表不同的位置。 相同优先级的任务插入就绪列表中的同一条链表中。 要想任务支持优先级&#xff0c;即只要实现在任务切换&#xf…

【C++篇】C++入门基础(二)

&#x1f4ac; 欢迎讨论&#xff1a;在阅读过程中有任何疑问&#xff0c;欢迎在评论区留言&#xff0c;我们一起交流学习&#xff01; &#x1f44d; 点赞、收藏与分享&#xff1a;如果你觉得这篇文章对你有帮助&#xff0c;记得点赞、收藏&#xff0c;并分享给更多对C感兴趣的…

Mysql架构之日志讲解:redo log,undo log,bin log 日志

一、buffer pool缓冲区 讲日志之前&#xff0c;我们要先认识一下buffer pool缓冲区。 mysql想完成数据的修改&#xff0c;会先从存储引擎层读取数据&#xff0c;把数据读取到服务层进行数据的修改&#xff0c;再通过存储引擎层把数据更新到数据库中。 mysql每次读取数据都会…

容器主机CPU使用率突增问题一则

关键词 LINUX、文件系统crontab 、mlocate根目录使用率 There are many things that can not be broken&#xff01; 如果觉得本文对你有帮助&#xff0c;欢迎点赞、收藏、评论&#xff01; 一、问题现象 业务一台容器服务器&#xff0c;近期经常收到cpu不定期抖动告警&#x…

simpleITK - Setup - matplotlib‘s imshow

使用 matplotlib 显示内联图像 在此笔记本中&#xff0c;我们将探索使用 matplotlib 显示笔记本中的图像&#xff0c;并致力于开发可重复使用的函数来显示 SimpleITK 图像的 2D、3D、颜色和标签叠加层。 我们还将研究使用需要输入图像重叠的图像过滤器的微妙之处。 %matplot…