表达式
68 分钟阅读
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.
限定标识符可以在不同包中访问一个标识符,但该标识符所在的包必须已经被导入。该标识符必须可被导出并在该包的package block中声明。
|
|
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.
复合字面量每次被求值时都会构造新的复合值。它们由字面量的类型和一个由花括号组成的元素列表组成
。每个元素可以选择在前面加上一个相应的键。
|
|
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.
LiteralType 的核心类型T
必须是一个结构体、数组、切片或映射类型(语法会强制执行这个约束,除非当类型是作为TypeName给出时)。元素和键的类型必须可分配给T
类型的对应字段、元素和键类型;不需要进行额外的转换。这里的键被解释为结构体字面量的字段名、数组字面量或切片字面量的索引、映射字面量的键。对于映射字面量,所有的元素必须有一个键。用相同的字段名或常量键值指定多个元素是错误的。对于非常量的映射键,请参见关于求值顺序的章节。
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
给定声明:
|
|
one may write
我们可以这样写:
|
|
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. - 带键的元素使用该键作为其索引。键必须是一个可由
int
类型的值表示的非负常数;如果它是有类型的,则它必须是整数类型。 - An element without a key uses the previous element’s index plus one. If the first element has no key, its index is zero.
- 不带键的元素使用前一个元素的索引加1。如果第一个元素没有键,它的索引是0。
Taking the address of a composite literal generates a pointer to a unique variable initialized with the literal’s value.
对一个复合字面量取址会产生一个指向唯一变量的指针,该变量用字面量的值初始化。
|
|
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.
请注意,切片或映射类型的零值与同一类型的初始化过但为空的值不同。因此,获取一个空切片或空映射复合字面量的地址与用new分配一个新的切片或映射值的效果不同。
|
|
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.
数组字面量的长度是字面量类型中指定的长度。如果在字面量上提供的元素少于长度,缺少的元素将被设置为数组元素类型的零值。若提供的元素的索引值超出了数组的索引范围,将导致错误。标记法...
指定的数组长度等于最大元素索引加1。
|
|
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
切片字面量描述了整个底层数组字面量。因此,切片字面量的长度和容量是最大元素索引加1。切片字面量的形式是:
|
|
and is shorthand for a slice operation applied to an array:
是对数组进行切片操作的简写:
|
|
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
.
在一个数组、切片或映射类型T
的复合字面量中,如果本身是复合字面量的元素或映射键与T
的元素或键类型相同,则可以省略(元素或映射键的)相应的字面量类型。同样,当元素或键类型为*T
时,作为复合字面量地址的元素或键可以省略&T
。
|
|
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.
当使用LiteralType的TypeName形式的复合字面量作为操作数
出现在关键字和 “if
"、"for
“或 “switch
“等语句块的左花括号
之间,并且复合字面量没有被括在圆括号、方括号或花括号中时,会出现解析歧义。在这种罕见的情况下,字面量的左花括号被错误地解析为引入语句块的左花括号。为了解决这个问题,复合字面量必须出现在圆括号
内。
|
|
Examples of valid array, slice, and map literals:
有效的数组、切片和映射字面量的例子:
|
|
Function literals 函数字面量
A function literal represents an anonymous function. Function literals cannot declare type parameters.
函数字面量表示一个匿名函数。函数字面量不能声明类型参数
。
|
|
|
|
A function literal can be assigned to a variable or invoked directly.
函数字面量可以被分配给一个变量或直接调用。
|
|
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.
主表达式是一元、二元表达式的操作数。
|
|
|
|
Selectors 选择器
For a primary expression x
that is not a package name, the selector expression
|
|
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.
表示值x
(有时是*x
;见下文)的字段或方法f
。标识符f
被称为(字段或方法)选择器
;它不能是空白标识符。选择器表达式的类型是f
的类型。若x
是包名,请参见关于限定标识符一节。
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.
选择器f
可以表示类型T
的f
字段或f
方法,也可以指代T
的嵌入字段或嵌入方法f
。在T
的一个嵌入字段A
中声明的字段或方法f
的深度是A
中f
的深度加1。
The following rules apply to selectors:
以下规则适用于选择器:
For a value
x
of typeT
or*T
whereT
is not a pointer or interface type,x.f
denotes the field or method at the shallowest depth inT
where there is such anf
. If there is not exactly onef
with shallowest depth, the selector expression is illegal.对于类型为
T
或*T
(T
不是指针或接口类型)的值x
,x.f
表示T
中存在这样一个最浅深度的字段或方法f
。如果不是恰好有仅有一个f
在最浅深度的话,那么选择器表达式是非法的。个人注释
这里额的仅有一个是说在最浅深度!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
package main import "fmt" type A struct { Name string } type B struct { A Name string } func main() { st := B{A: A{Name: "a"}, Name: "b"} fmt.Printf("%+v\n", st) // {A:{Name:a} Name:b} fmt.Printf("%#v\n", st) // main.B{A:main.A{Name:"a"}, Name:"b"} fmt.Printf("%v\n", st.Name) // b }
For a value
x
of typeI
whereI
is an interface type,x.f
denotes the actual method with namef
of the dynamic value ofx
. If there is no method with namef
in the method set ofI
, the selector expression is illegal.对于接口类型
I
的值x
,x.f
表示动态值x
的名为f
的实际方法。如果在I
的方法集中没有名为f
的方法,那么选择器表达式是非法的。个人注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
package main import "fmt" type Adder interface { Add(int, int) int } type Op struct { } func (o Op) Add(a int, b int) (total int) { return a + b } func main() { var i Adder a := Op{} fmt.Printf("%+v\n", i) //<nil> fmt.Printf("%#v\n", a) //main.Op{} i = a fmt.Printf("%v\n", i.Add(1, 2)) // 3 //fmt.Printf("%v\n", i.Sub(1, 2)) // i.Sub undefined (type Adder has no field or method Sub) }
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
.作为例外,如果
x
的类型是一个已定义的指针类型,并且(*x).f
是一个有效的表示一个字段(不是一个方法)的选择器表达式,那么x.f
是(*x).f
的简写。个人注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
package main import "fmt" type Person struct { Name *string Age int } func main() { name1 := "zlongx1" name2 := "zlongx2" v := Person{Name: &name1, Age: 31} p := &Person{Name: &name2, Age: 32} fmt.Printf("%#v\n", v) // main.Person{Name:(*string)(0xc00004e270)} fmt.Printf("%#v\n", p) // &main.Person{Name:(*string)(0xc00004e280)} fmt.Printf("%#v\n", *p) // main.Person{Name:(*string)(0xc00004e280)} fmt.Printf("%#v\n", v.Name) // (*string)(0xc00004e270) fmt.Printf("%#v\n", p.Name) // (*string)(0xc00004e280) fmt.Printf("%#v\n", (*p).Name) // (*string)(0xc00004e280) fmt.Printf("%#v\n", v.Age) // 31 fmt.Printf("%#v\n", p.Age) // 32 fmt.Printf("%#v\n", (*p).Age) // 32 }
In all other cases,
x.f
is illegal.在所有其它情况下,
x.f
是非法的。If
x
is of pointer type and has the valuenil
andx.f
denotes a struct field, assigning to or evaluatingx.f
causes a run-time panic.如果
x
是值为nil
的指针类型,并且x.f
表示一个结构体字段,那么赋值或计算x.f
会引起运行时恐慌。
个人注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
package main import "fmt" type Person struct { Name string Age int } func main() { var a Person var b *Person fmt.Printf("%#v\n", a) //main.Person{Name:"", Age:0} fmt.Printf("%#v\n", b) //(*main.Person)(nil) fmt.Printf("Name=\"%v\"\n", a.Name) //Name="" a.Name = "a" fmt.Printf("Name=\"%v\"\n", a.Name) //Name="a" //b.Name = "b" // runtime error: invalid memory address or nil pointer dereference //fmt.Printf("Name=\"%v\"\n", b.Name) //panic: runtime error: invalid memory address or nil pointer dereference }
If
x
is of interface type and has the valuenil
, calling or evaluating the methodx.f
causes a run-time panic.如果
x
是值为nil
的接口类型,那么调用或计值x.f
方法会引起运行时恐慌。个人注释
1 2 3 4 5 6 7 8 9 10 11 12 13
package main import "fmt" type Adder interface { Add(int, int) int } func main() { var i Adder fmt.Printf("%#v\n", i) //<nil> fmt.Printf("%v\n", i.Add(1, 2)) //panic: runtime error: invalid memory address or nil pointer dereference }
For example, given the declarations:
例如,给定声明:
|
|
one may write:
则我们可以这样写:
|
|
but the following is invalid:
但下面的内容是无效的:
|
|
个人注释
以上示例的完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
package main import ( "fmt" "reflect" ) type T0 struct { x int } func (*T0) M0() string { return "in M0 func" } type T1 struct { y int } func (T1) M1() string { return "in M1 func" } type T2 struct { z int T1 *T0 } func (*T2) M2() string { return "in M2 func" } type Q *T2 func DumpMethodSet(i interface{}) { v := reflect.TypeOf(i) elemTyp := v.Elem() n := elemTyp.NumMethod() if n == 0 { fmt.Printf("%s's method set is empty!\n", elemTyp) } fmt.Printf("%s's method set:\n", elemTyp) for j := 0; j < n; j++ { fmt.Println("-", elemTyp.Method(j).Name) } fmt.Println() } func main() { var t T2 = T2{T0: &T0{}} var p *T2 = &T2{T0: &T0{}} var q Q = p fmt.Printf("t=%+v\n", t) // t={z:0 T1:{y:0} T0:0xc00001a0a8} fmt.Printf("p=%+v\n", p) // p=&{z:0 T1:{y:0} T0:0xc00001a0c0} fmt.Printf("q=%+v\n", q) // q=&{z:0 T1:{y:0} T0:0xc00001a0c0} //main.T2's method set: //- M0 //- M1 DumpMethodSet(&t) //*main.T2's method set: //- M0 //- M1 //- M2 DumpMethodSet(&p) //main.Q's method set is empty! //main.Q's method set: DumpMethodSet(&q) //main.T2's method set: //- M0 //- M1 DumpMethodSet(&(*q)) fmt.Printf("t.z=%v\n", t.z) // t.z=0 fmt.Printf("t.y=%v\n", t.y) // t.y=0 fmt.Printf("t.x=%v\n", t.x) // t.x=0 fmt.Printf("p.z=%v\n", p.z) // p.z=0 fmt.Printf("p.y=%v\n", p.y) // p.y=0 fmt.Printf("p.x=%v\n", p.x) // p.x=0 fmt.Printf("q.z=%v\n", q.z) // q.z=0 fmt.Printf("q.y=%v\n", q.y) // q.y=0 fmt.Printf("q.x=%v\n", q.x) // q.x=0 fmt.Printf("t.M0()=%v\n", t.M0()) // t.M0()=in M0 func fmt.Printf("t.M1()=%v\n", t.M1()) // t.M1()=in M1 func fmt.Printf("t.M2()=%v\n", t.M2()) // t.M2()=in M2 func fmt.Printf("p.M0()=%v\n", p.M0()) // p.M0()=in M0 func fmt.Printf("p.M1()=%v\n", p.M1()) // p.M1()=in M1 func fmt.Printf("p.M2()=%v\n", p.M2()) // p.M2()=in M2 func //fmt.Printf("q.M0()=%v\n", q.M0()) // q.M0 undefined (type Q has no field or method M0) //fmt.Printf("q.M1()=%v\n", q.M1()) // q.M1 undefined (type Q has no field or method M1) //fmt.Printf("q.M2()=%v\n", q.M2()) // q.M2 undefined (type Q has no field or method M2) fmt.Printf("(*q).M0()=%v\n", (*q).M0()) // (*q).M0()=in M0 func fmt.Printf("(*q).M1()=%v\n", (*q).M1()) // (*q).M1()=in M1 func fmt.Printf("(*q).M2()=%v\n", (*q).M2()) // (*q).M2()=in M2 func }
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.
如果M
在类型T
的方法集中,那么T.M
是一个可以作为普通函数来调用的函数,其实参与M
相同,不过其前缀有一个额外的(作为该方法的接收器的)实参。
|
|
Consider a struct type T
with two methods, Mv
, whose receiver is of type T
, and Mp
, whose receiver is of type *T
.
考虑一个结构体类型T
,它有两个方法:Mv
,其接收器类型为T
,和Mp
,其接收器类型为*T
。
|
|
The expression
表达式:
|
|
yields a function equivalent to Mv
but with an explicit receiver as its first argument; it has signature
生成一个与Mv
等价的函数,但是它的第一个实参是一个显式的接收器;它具有以下签名:
|
|
That function may be called normally with an explicit receiver, so these five invocations are equivalent:
该函数可以在带有一个显式接收器的情况下正常调用,因此如下这五种调用是等同的:
|
|
Similarly, the expression
类似地,表达式
|
|
yields a function value representing Mp
with signature
生成一个代表Mp
的函数值,它的签名是:
|
|
For a method with a value receiver, one can derive a function with an explicit pointer receiver, so
对于一个带值接收器
的方法,可以推导出
一个带有显式指针接收器的函数,因此
|
|
yields a function value representing Mv
with signature
生成了一个代表Mv
的函数值,它的签名是:
|
|
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.
最后一种情况,将一个带指针接收器的方法当做
一个带值接收器的函数,是非法的,因为指针接收器的方法不在值类型的方法集中。
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.
从方法中推导出来的函数值是用函数调用语法
来调用的;接收器被作为调用的第一个实参来提供。也就是说,f := T.Mv
中的f
是作为f(t, 7)
被调用,而不是t.f(7)
。要构造一个绑定接收器的函数,可以使用函数字面量或方法值。
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.
从接口类型的方法中推导出来函数值是合法的。这样的函数需要一个该接口类型的显式接收器。
个人注释
以上示例的完整代码如下:TODO
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
package main import "fmt" type T struct { a int } func (tv T) Mv(a int) int { return a + tv.a } func (tp *T) Mp(f float32) float32 { return f + float32(tp.a) } func funcMv1(tv T, a int) int { return tv.a + a } func funcMv2(tv *T, a int) int { return tv.a + a } func funcMp(tp *T, a float32) float32 { return float32(tp.a) + a } func main() { var tv T = T{} var tp *T = &T{} tv.a = 1 fmt.Printf("tv.Mv(7)=%v\n", tv.Mv(7)) // tv.Mv(7)=8 fmt.Printf("T.Mv(tv, 7)=%v\n", T.Mv(tv, 7)) // T.Mv(tv, 7)=8 fmt.Printf("(T).Mv(tv, 7)=%v\n", (T).Mv(tv, 7)) // (T).Mv(tv, 7)=8 fmt.Printf("(*T).Mv(&tv, 7)=%v\n", (*T).Mv(&tv, 7)) // (*T).Mv(&tv, 7)=8 fmt.Printf("tv.Mp(7)=%v\n", tv.Mp(7)) // tv.Mp(7)=8 fmt.Println("-- a=1 ---------------------------------") tv.a = 2 f1 := T.Mv fmt.Printf("f1(tv, 7)=%v\n", f1(tv, 7)) // f1(tv, 7)=9 fmt.Printf("funcMv1(tv, 7)=%v\n", funcMv1(tv, 7)) // funcMv1(tv, 7)=9 fmt.Println("-- a=2 ---------------------------------") tv.a = 3 f2 := (*T).Mv fmt.Printf("f2(&tv, 7)=%v\n", f2(&tv, 7)) // f2(&tv, 7)=10 fmt.Printf("funcMv2(&tv, 7)=%v\n", funcMv2(&tv, 7)) // funcMv2(&tv, 7)=10 fmt.Println("-- a=3 ---------------------------------") tv.a = 4 tp.a = 4 fmt.Printf("tp.Mv(7)=%v\n", tp.Mv(7)) // tp.Mv(7)=11 fmt.Printf("tp.Mp(7)=%v\n", tp.Mp(7)) // tp.Mp(7)=11 fmt.Printf("(*T).Mp(tp, 7)=%v\n", (*T).Mp(tp, 7)) // (*T).Mp(tp, 7)=11 fmt.Printf("(*T).Mv(tp, 7)=%v\n", (*T).Mv(tp, 7)) // (*T).Mv(tp, 7)=11 fmt.Printf("(*T).Mv(&tv, 7)=%v\n", (*T).Mv(&tv, 7)) // (*T).Mv(&tv, 7)=11 fmt.Println("-- a=4 ---------------------------------") tv.a = 5 tp.a = 5 f3 := (*T).Mp fmt.Printf("f3(&tv, 7)=%v\n", f3(&tv, 7)) // f3(&tv, 7)=12 fmt.Printf("f3(tp, 7)=%v\n", f3(tp, 7)) // f3(tp, 7)=12 fmt.Printf("funcMp(&tv, 7)=%v\n", funcMp(&tv, 7)) // funcMp(&tv, 7)=12 fmt.Printf("funcMp(tp, 7)=%v\n", funcMp(tp, 7)) // funcMp(tp, 7)=12 fmt.Println("-- a=5 ---------------------------------") }
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.
如果表达式x
有静态类型T
,并且M
在类型T
的方法集中,那么x.M
被称为一个方法值
。方法值x.M
是一个可调用的函数值,其实参与x.M
的方法调用相同。表达式x
在方法值的求值过程中被求值 、 保存;然后保存的副本被用作任何调用中的接收器上,这些调用可能在以后执行。
|
|
The type T
may be an interface or non-interface type.
类型T
既可以是接口类型,也可以是非接口类型。
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
.
如同上面对方法表达式的讨论,考虑一个结构体类型T
,它有两个方法:Mv
,其接收器类型为T
,和Mp
,其接收器类型为*T
。
|
|
The expression
表达式
|
|
yields a function value of type
生成一个类型如下的函数值:
|
|
These two invocations are equivalent:
这两种调用是等价的:
|
|
Similarly, the expression
同样地,表达式
|
|
yields a function value of type
生成一个类型如下的函数值:
|
|
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
.
和选择器一样,若对以值作为接收器的非接口方法,使用指针来引用,则(Go语言)将自动解除对该指针的引用:pt.Mv
等同于(*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
.
和方法调用一样,若对以指针作为接收器的非接口方法,使用可寻址的值来引用,则(Go语言)将自动获取该值的地址:t.Mp
等同于(&t).Mp
。
|
|
个人注释
以上示例的完整代码如下:
Although the examples above use non-interface types, it is also legal to create a method value from a value of interface type.
尽管上面的例子使用了非接口类型,但从接口类型的值中创建一个方法值也是合法的。
|
|
个人注释
以上示例的完整代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
package main import "fmt" type MyVal struct{} func (m MyVal) M(a int) int { return a } func main() { myVal := MyVal{} var i interface{ M(int) int } = myVal f := i.M fmt.Printf("%v\n", f(7)) // 7 fmt.Printf("%v\n", i.M(7)) // 7 }
Index expressions 索引表达式
A primary expression of the form
若主表达式的形式是:
|
|
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:
则表示可用x
来检索的数组a
、数组指针a
、切片a
、字符串a
或映射a
的元素,x
分别被称为索引
或映射键
。以下规则适用:
If a
is neither a map nor a type parameter:
如果a
既不是映射也不是类型参数:
- the index
x
must be an untyped constant or its core type must be an integer - 索引
x
必须是一个无类型的常量,或者其核心类型必须是整数类型 - a constant index must be non-negative and representable by a value of type
int
- 常量索引必须是非负且可以用
int
类型的值来表示 - a constant index that is untyped is given type
int
- 无类型常量索引会被赋予
int
类型。 - the index
x
is in range if0 <= x < len(a)
, otherwise it is out of range - 如果
0 <= x < len(a)
,则索引x
在范围内,否则就超出了范围。
For a
of array type A
:
对于数组类型A
的a
:
- a constant index must be in range
- 常量索引必须在范围内
- if
x
is out of range at run time, a run-time panic occurs - 如果
x
在运行时超出了范围,就会发生运行时恐慌 - a
is the array element at index
xand the type of
ais the element type of
A a[x]
是索引为x
的数组元素,a[x]
的类型是A
的元素类型。
For a
of pointer to array type:
对于数组类型指针的a
:
- a
is shorthand for
(*a) a[x]
是(*a)[x]
的简写
For a
of slice type S
:
对于切片类型S
的a
:
- if
x
is out of range at run time, a run-time panic occurs - 如果
x
在运行时超出了范围,就会发生运行时恐慌 - a
is the slice element at index
xand the type of
ais the element type of
S a[x]
是索引x
处的切片元素,a[x]
的类型是S
的元素类型。
For a
of string type:
对于字符串类型的a
:
- a constant index must be in range if the string
a
is also constant - 如果字符串
a
是常量,那么常量索引必须在范围内 - if
x
is out of range at run time, a run-time panic occurs - 如果
x
在运行时超出了范围,就会发生运行时恐慌 a[x]
is the non-constant byte value at indexx
and the type ofa[x]
is `bytea[x]
是索引x
处的非常量字节值,a[x]
的类型是byte
。a[x]
may not be assigned toa[x]
不能被赋值
For a
of map type M
:
对于映射类型为M
的a
:
x
’s type must be assignable to the key type of `Mx
的类型必须可以被分配给M
的键类型- if the map contains an entry with key
x
,a[x]
is the map element with keyx
and the type ofa[x]
is the element type ofM
- 如果映射中有键
x
的项,那么a[x]
就是键x
的映射元素,a[x]
的类型就是M
的元素类型 - if the map is
nil
or does not contain such an entry,a[x]
is the zero value for the element type ofM
- 如果映射为
nil
或者不包含任何项,a[x]
是M
的元素类型的零值。
For a
of type parameter type P
:
对于参数类型为P
的a
:
- The index expression
a[x]
must be valid for values of all types inP
’s type set. - 索引表达式
a[x]
必须对P
的类型集中的所有类型的值有效。 - The element types of all types in
P
’s type set must be identical. In this context, the element type of a string type isbyte
. P
的类型集中所有类型的元素类型必须是相同的。在此上下文中,字符串类型的元素类型是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. - 如果在
P
的类型集中有一个映射类型,那么该类型集中的所有类型必须是映射类型,且对应的键类型必须都是一致的。 a[x]
is the array, slice, or string element at indexx
, or the map element with keyx
of the type argument thatP
is instantiated with, and the type ofa[x]
is the type of the (identical) element types.a[x]
是索引为x
的数组、切片或字符串元素,或者P
实例化的类型实参中键为x
的映射元素,a[x]
的类型是(一致的)元素类型的类型。a[x]
may not be assigned to ifP
’s type set includes string types.- 如果
P
的类型集包括字符串类型,则a[x]
不能再被赋值。
Otherwise a[x]
is illegal.
否则a[x]
是非法的。
An index expression on a map a
of type map[K]V
used in an assignment statement or initialization of the special form
若将类型为map[K]V
的映射a
上的索引表达式使用在赋值语句或特殊格式的初始化中:
|
|
yields an additional untyped boolean value. The value of ok
is true
if the key x
is present in the map, and false
otherwise.
将产生一个额外的无类型
布尔值。如果键x
存在于映射中,ok
的值为true
,否则为false
。
Assigning to an element of a nil
map causes a run-time panic.
若给nil
映射的元素赋值,将导致运行时恐慌。
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.
切片表达式从一个字符串、数组、数组指针或切片中构造一个子串或切片。有两种变体:一种是指定低位和高位边界的简单形式,另一种是同时也指定容量的完整形式。
个人注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
package main import "fmt" func main() { str1 := "abcdefghijklmn" s1 := str1[0:3] //s2 := str[0:3:4] // invalid operation: 3-index slice of string fmt.Printf("%T,str1=%v\n", str1, str1) // string,str1=abcdefghijklmn fmt.Printf("%T,s1=%v\n", s1, s1) // string,s1=abc arr1 := [...]int{0, 1, 2, 3, 4, 5} s20 := arr1[0:3] s21 := arr1[0:3:5] fmt.Printf("%T,arr1=%v,len=%d,cap=%d\n", arr1, arr1, len(arr1), cap(arr1)) // [6]int,arr1=[0 1 2 3 4 5],len=6,cap=6 fmt.Printf("%T,s20=%v,len=%d,cap=%d\n", s20, s20, len(s20), cap(s20)) // []int,s20=[0 1 2],len=3,cap=6 fmt.Printf("%T,s21=%v,len=%d,cap=%d\n", s21, s21, len(s21), cap(s21)) // []int,s21=[0 1 2],len=3,cap=5 sli := []int{0, 1, 2, 3, 4, 5} s30 := sli[0:3] s31 := sli[0:3:5] fmt.Printf("%T,sli=%v,len=%d,cap=%d\n", sli, sli, len(sli), cap(sli)) // []int,sli=[0 1 2 3 4 5],len=6,cap=6 fmt.Printf("%T,s30=%v,len=%d,cap=%d\n", s30, s30, len(s30), cap(s30)) // []int,s30=[0 1 2],len=3,cap=6 fmt.Printf("%T,s31=%v,len=%d,cap=%d\n", s31, s31, len(s31), cap(s31)) // []int,s31=[0 1 2],len=3,cap=6 arr2 := &[...]int{0, 1, 2, 3, 4, 5} s40 := arr2[0:3] s41 := arr2[0:3:5] s40_1 := (*arr2)[0:3] s41_1 := (*arr2)[0:3:5] fmt.Printf("%T,arr2=%v,len=%d,cap=%d\n", arr2, arr2, len(arr2), cap(arr2)) // *[6]int,arr2=&[0 1 2 3 4 5],len=6,cap=6 fmt.Printf("%T,s40=%v,len=%d,cap=%d\n", s40, s40, len(s40), cap(s40)) // []int,s40=[0 1 2],len=3,cap=6 fmt.Printf("%T,s41=%v,len=%d,cap=%d\n", s41, s41, len(s41), cap(s41)) // []int,s41=[0 1 2],len=3,cap=5 fmt.Printf("%T,s40_1=%v,len=%d,cap=%d\n", s40_1, s40_1, len(s40_1), cap(s40_1)) // []int,s40_1=[0 1 2],len=3,cap=6 fmt.Printf("%T,s41_1=%v,len=%d,cap=%d\n", s41_1, s41_1, len(s41_1), cap(s41_1)) // []int,s41_0=[0 1 2],len=3,cap=5 str2 := "我爱我的祖国" s2 := str2[0:3] s3 := str2[0:4] s4 := str2[0:5] //s5 := str2[0:3:4] // invalid operation: 3-index slice of string fmt.Printf("%T,str2=%v\n", str2, str2) // string,str2=我爱我的祖国 fmt.Printf("%T,s2=%v\n", s2, s2) // string,s2=我 fmt.Printf("%T,s3=%v\n", s3, s3) // string,s3=我� fmt.Printf("%T,s4=%v\n", s4, s4) // string,s4=我�� s50 := "I love you!"[0:6] fmt.Printf("%T,s50=%v\n", s50, s50) // string,s50=I love var nilSli []int s60 := nilSli[0:] fmt.Printf("%T,nilSli=%v,len=%d,cap=%d,%t\n", nilSli, nilSli, len(nilSli), cap(nilSli), nilSli == nil) // []int,nilSli=[],len=0,cap=0,true fmt.Printf("%T,s60=%v,len=%d,cap=%d,%t\n", s60, s60, len(s60), cap(s60), s60 == nil) // []int,s60=[],len=0,cap=0,true }
Simple slice expressions 简单切片表达式
The primary expression
主表达式
|
|
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
的核心类型必须是字符串、数组、数组指针、切片或者bytestring。low
和high
所在的索引选择了哪些元素显示在操作数a
的结果中。若结果的索引从0开始,则长度等于high
减去 low
。在对数组a
进行切片后
|
|
the slice s
has type []int
, length 3, capacity 4, and elements
切片s
有类型[]int
,长度3,容量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:
为方便起见,任何索引都可以被省略。缺少的low
索引默认为0;缺少的high
索引默认为被切片的操作数的长度:
|
|
If a
is a pointer to an array, a[low : high]
is shorthand for (*a)[low : high]
.
如果a
是一个数组指针,则a[low:high]
是(*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.
对于数组或字符串,如果0 <= low <= high <= len(a)
,则索引在范围内,否则就超出了范围。对于切片,索引的上限是切片的容量cap(a)
,而不是长度。常量索引必须是非负的,并且可以用int
类型的值来表示;对于数组或字符串常量,常量索引也必须在范围内。如果两个索引都是常量,它们必须满足low <= high
。如果索引在运行时超出范围,就会发生运行时恐慌。
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.
除了无类型字符串外:
如果被切片的操作数是字符串或切片,则切片的操作结果是一个与操作数相同类型的非常量值。
如果被切片的操作数是无类型的字符串,则切片的操作结果是一个
string
类型的非常量值。如果被切片的操作数是(必须可被寻址的)数组,则切片的操作结果是一个与数组的元素类型一致的切片。
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.
如果有效切片表达式的切片操作数是nil
切片,那么切片的操作结果就是一个nil
切片。否则,如果切片的操作结果是一个切片,则它与操作数共享底层数组。
|
|
Full slice expressions 完整的切片表达式
The primary expression
主表达式
|
|
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[low : high]
相同类型的切片,并且具有相同的长度和元素。此外,它通过将结果切片设置为max 减去 low
的容量。a
的核心类型必须是数组,数组指针,或者切片(但不是字符串)。在对数组a
进行切分后
|
|
the slice t
has type []int
, length 2, capacity 4, and elements
切片t
有类型[]int
,长度2,容量4,以及元素
|
|
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.
与简单切片表达式一样,如果a
是一个数组指针,则a[low:high:max]
是(*a)[low:high:max]
的简写。如果切片的操作数是一个数组,它必须是可被寻址的。
个人注释
给出切片的操作数是数组,但不能被寻址的示例:
1 2 3 4 5 6 7 8
package main import "fmt" func main() { s := ([...]int{0, 1, 2, 3})[0:2] // invalid operation: ([...]int{…}) (value of type [4]int) (slice of unaddressable value) fmt.Println(s) }
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.
如果0 <= low <= high <= max <= cap(a)
,则索引就在范围内,否则就超出了范围。常量索引必须是非负数,并且可以用int
类型的值来表示;对于数组,常量索引也必须在范围内。如果多个索引是常量,那么出现的常量必须在相对于彼此的范围内。如果索引在运行时超出了范围,就会发生运行时恐慌。
个人注释
完整的切片表达式又称:扩展的切片表达式,是在go1.2引入的,为的是限制新生成的切片的容量。
限制新生成的切片容量有什么好处?
给出示例说明下: (1)从数组中生成新的切片:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
package main import "fmt" func main() { a1 := [6]int{0, 1, 2, 3, 4, 5} a2 := [6]int{0, 1, 2, 3, 4, 5} s1 := a1[0:3] s2 := a2[0:3:3] fmt.Printf("a1=%#v\n", a1) fmt.Printf("s1=%#v\n", s1) fmt.Printf("a2=%#v\n", a2) fmt.Printf("s2=%#v\n", s2) fmt.Println("- apppend 22--------------------------------") s1 = append(s1, 22) s2 = append(s2, 22) fmt.Printf("a1=%#v\n", a1) fmt.Printf("s1=%#v\n", s1) fmt.Printf("a2=%#v\n", a2) fmt.Printf("s2=%#v\n", s2) fmt.Println("- apppend 33--------------------------------") s1 = append(s1, 33) s2 = append(s2, 33) fmt.Printf("a1=%#v\n", a1) fmt.Printf("s1=%#v\n", s1) fmt.Printf("a2=%#v\n", a2) fmt.Printf("s2=%#v\n", s2) } Output: a1=[6]int{0, 1, 2, 3, 4, 5} s1=[]int{0, 1, 2} a2=[6]int{0, 1, 2, 3, 4, 5} s2=[]int{0, 1, 2} - apppend 22-------------------------------- a1=[6]int{0, 1, 2, 22, 4, 5} s1=[]int{0, 1, 2, 22} a2=[6]int{0, 1, 2, 3, 4, 5} s2=[]int{0, 1, 2, 22} - apppend 33-------------------------------- a1=[6]int{0, 1, 2, 22, 33, 5} s1=[]int{0, 1, 2, 22, 33} a2=[6]int{0, 1, 2, 3, 4, 5} s2=[]int{0, 1, 2, 22, 33}
可以看到,s2在使用
arr2[0:3:3]
后,对切片s2进行的两次append
操作,都没有对原数组中的元素造成影响! (2)从切片中生成新的切片:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
package main import "fmt" func main() { sa := []int{0, 1, 2, 3, 4, 5} sb := []int{0, 1, 2, 3, 4, 5} s1 := sa[0:3] s2 := sb[0:3:3] fmt.Printf("sa=%#v\n", sa) fmt.Printf("s1=%#v\n", s1) fmt.Printf("sb=%#v\n", sb) fmt.Printf("s2=%#v\n", s2) fmt.Println("- apppend 22--------------------------------") s1 = append(s1, 22) s2 = append(s2, 22) fmt.Printf("sa=%#v\n", sa) fmt.Printf("s1=%#v\n", s1) fmt.Printf("sb=%#v\n", sb) fmt.Printf("s2=%#v\n", s2) fmt.Println("- apppend 33--------------------------------") s1 = append(s1, 33) s2 = append(s2, 33) fmt.Printf("sa=%#v\n", sa) fmt.Printf("s1=%#v\n", s1) fmt.Printf("sb=%#v\n", sb) fmt.Printf("s2=%#v\n", s2) } Output: sa=[]int{0, 1, 2, 3, 4, 5} s1=[]int{0, 1, 2} sb=[]int{0, 1, 2, 3, 4, 5} s2=[]int{0, 1, 2} - apppend 22-------------------------------- sa=[]int{0, 1, 2, 22, 4, 5} s1=[]int{0, 1, 2, 22} sb=[]int{0, 1, 2, 3, 4, 5} s2=[]int{0, 1, 2, 22} - apppend 33-------------------------------- sa=[]int{0, 1, 2, 22, 33, 5} s1=[]int{0, 1, 2, 22, 33} sb=[]int{0, 1, 2, 3, 4, 5} s2=[]int{0, 1, 2, 22, 33}
也可以看到,s2在使用
sb[0:3:3]
后,对切片s2进行的两次append
操作,都没有对原切片中的元素造成影响! 这么说,只要切片的容量改变(无论是否是使用简单切片表达式,还是完整的切片表达式),就与原数组或原切片没有了联系。
Type assertions 类型断言
For an expression x
of interface type, but not a type parameter, and a type T
, the primary expression
|
|
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.
断言了x
不是nil
,并且x
中存储的值是T
类型。标记法x.(T)
被称为类型断言
。
More precisely, if T
is not an interface type, x.(T)
asserts that the dynamic type of x
is identical to the type 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
.
更确切地说,如果T
不是接口类型,则x.(T)
断言x
的动态类型与T
的类型一致。在这种情况下,T
必须实现x
的(接口)类型;否则类型断言是无效的,因为对于x
来说存储T
类型的值是不可能的。如果T
是一个接口类型,则x.(T)
断言x
的动态类型实现了接口T
。
个人注释
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
package main import "fmt" type T1 interface{ Set() } type St1 struct{} func (s St1) Set() { fmt.Println("called Set") } type MyInt int func main() { // T 是接口的情况 // 断言无效 var i1 interface{ Set() } v, ok := i1.(interface{ Set() }) fmt.Printf("%T,%#v,%t\n", i1, v, ok) // <nil>,<nil>,false // 断言无效 var i2 T1 v, ok = i2.(T1) fmt.Printf("%T,%#v,%t\n", i2, v, ok) // <nil>,<nil>,false //var i3 St1 //v, ok = i3.(T1) // invalid operation: i3 (variable of type St1) is not an interface //fmt.Printf("%T,%#v,%t", i3, v, ok) //i4 := St1{} //v, ok = i4.(T1) // invalid operation: i4 (variable of type St1) is not an interface //fmt.Printf("%T,%#v,%t", i4, v, ok) //断言: i5 的动态类型实现了 T1 接口 var i5 interface{} i5 = St1{} v, ok = i5.(T1) fmt.Printf("%T,%#v,%t\n", i5, v, ok) // main.St1,main.St1{},true // T 不是接口的情况 //i20 := MyInt(10) //v, ok = i20.(int) // invalid operation: i20 (variable of type MyInt) is not an interface //fmt.Printf("%T,%#v,%t\n", i20, v, ok) //var i21 interface{} //i21 = MyInt(10) //v, ok = i21.(int) // cannot use i21.(int) (value of type int) as interface{Set()} value in assignment: int does not implement interface{Set()} (missing method Set) //fmt.Printf("%T,%#v,%t\n", i21, v, ok) //i22 := St1{} //v, ok = i22.(St1) // invalid operation: i22 (variable of type St1) is not an interface //fmt.Printf("%T,%#v,%t\n", i22, v, ok) //断言: i23 的动态类型与 St1 类型一致 var i23 interface{} i23 = St1{} v, ok = i23.(St1) fmt.Printf("%T,%#v,%t\n", i23, v, ok) // main.St1,main.St1{},true // 断言无效 var i24 T1 v, ok = i24.(St1) fmt.Printf("%T,%#v,%t\n", i24, v, ok) // <nil>,main.St1{},false //断言: i25 的动态类型与 St1 类型一致 var i25 T1 i25 = St1{} v, ok = i25.(St1) fmt.Printf("%T,%#v,%t\n", i25, v, ok) // main.St1,main.St1{},true }
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.
如果类型断言成立,表达式的值就是存储在x
中的值,其类型是T
。 如果类型断言不成立,就会发生运行时恐慌。换句话说,尽管x
的动态类型只有在运行时才知道,但在一个正确的程序中,x.(T)
的类型是已知的,是T
。
|
|
A type assertion used in an assignment statement or initialization of the special form
在赋值语句或特殊格式的初始化中使用的类型断言
|
|
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.
将产生一个额外的无类型布尔值。如果断言成立,ok
的值为true
。否则为false
,并且v
的值是T
类型的零值。在这种情况下不会
发生运行时恐慌。
Calls 调用
Given an expression f
with a core type F
of function type,
|
|
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.
带实参a1, a2, ... an
调用了f
。除了一种特殊情况以外,实参必须是可分配给F
的参数类型的单值表达式,并且在函数被调用之前被求值。该表达式的类型是F
的结果类型。方法调用是类似的,但是方法本身被指定为一个(在该方法的接收器类型的值上的)选择器。
|
|
If f
denotes a generic function, it must be instantiated before it can be called or used as a function value.
如果f
表示一个泛型函数,在它被调用或作为函数值使用之前,必须将其实例化。
In a function call, the function value and arguments are evaluated in the usual order. After they are evaluated, the parameters of the call are passed by value to the function and the called function begins execution. The return parameters of the function are passed by value back to the caller when the function returns.
在函数调用中,函数值和实参以通常的顺序被求值。在它们被求值之后,调用的参数被按值传递给函数,然后被调用的函数开始执行。当函数返回时,函数的返回参数按值传递给调用者。
Calling a nil
function value causes a run-time panic.
调用一个nil
的函数值会引起运行时恐慌。
个人注释
给出一个调用
nil
函数值的示例:
1 2 3 4 5 6 7 8 9 10
package main import "fmt" func main() { var f func() fmt.Println(f) // <nil> f() // panic: runtime error: invalid memory address or nil pointer dereference }
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 binding 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.
作为一个特例,如果一个函数或方法的返回值g
在数量上相等,并且可以单独分配给另一个函数或方法f
的参数,那么调用f(g(parameters_of_g))
将在把g
的返回值依次绑定到f
的参数后再调用f
。对f
的调用必须不包含对g
的调用以外的参数,并且g
必须至少有一个返回值。如果f
有一个在最后的...
参数,它将被分配给g
的(在分配完普通参数后所剩余的)返回值。
|
|
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()
:
如果x
的方法集(x
的类型)包含m
,并且实参列表可以分配给m
的参数列表,那么方法调用x.m()
是有效的。如果x
是可寻址的,并且&x
的方法集包含m
,则x.m()
就是(&x).m()
的简写:
|
|
There is no distinct method type and there are no method literals.
没有明确的方法类型,也没有方法字面量
。
个人注释
怎么理解在go语言规范中的:“没有明确的方法类型,也
没有方法字面量
。” ? 以下是ChatGPT的回答:
在Go语言规范中的这句话是指在Go语言中,方法不像函数那样具有明确的类型,并且无法直接创建方法的字面量。
在Go语言中,方法是与特定类型关联的函数,它们通过在函数签名前面添加接收者来定义。方法只能被与其关联的类型的实例调用,因为方法的接收者隐式地传递给方法。
举个例子:
1 2 3 4 5 6 7 8
goCopy codetype Rectangle struct { width int height int } func (r Rectangle) Area() int { return r.width * r.height }
在上述示例中,我们定义了一个
Rectangle
结构体类型和一个与之关联的方法Area
。方法Area
计算矩形的面积。 在Go语言规范中提到的"没有明确的方法类型"意味着方法不像函数那样具有独立的类型。例如,我们不能将一个方法赋值给一个函数变量或将其作为参数传递给其他函数。
另外,规范中提到的"没有方法字面量"意味着我们不能像函数那样直接创建表示方法的字面量。也就是说,我们不能像下面这样直接创建方法的值:
1 2 3 4 5 6 7 8
goCopy codefunc (r Rectangle) Area() int { return r.width * r.height } // 错误的方法字面量 methodLiteral := func (r Rectangle) Area() int { return r.width * r.height }
上面的代码是错误的,因为方法字面量是不被允许的。
总之,方法是与类型关联的函数,无法像函数那样拥有明确的类型。同时,我们也不能直接创建方法的字面量。方法只能通过与其关联的类型的实例进行调用。
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.
如果f
是带有一个...T
类型的位置在最后的参数p
的可变函数,那么在f
内部,p
的类型等同于[]T
类型。如果f
被调用时没有给p
的实参,传递给p
的值是nil
。否则,传递的值是一个新的[]T
类型的切片,这个切片带有一个新的底层数组,这个底层数组的连续元素作为实参,并且这些实参都必须可分配给T
。因此,切片的长度和容量等于绑定到p
的实参的数量,并且对每次调用(实参数量)都可能有所不同。
Given the function and calls
给出函数和调用
|
|
within Greeting
, who
will have the value nil
in the first call, and []string{"Joe", "Anna", "Eileen"}
in the second.
在Greeting
函数第一次被调用时,who
的值为nil
,在第二次被调用时,who
的值为[]string{"Joe", "Anna", "Eileen"}
。
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.
如果最后一个实参可以分配给切片类型[]T
,并且紧跟着...
,那么它将按原样传递作为 ...T
参数的值。在这种情况下,不会创建新的切片。
Given the slice s
and call
给定切片s
并调用
|
|
within Greeting
, who
will have the same value as s
with the same underlying array.
在Greeting
函数中,who
将拥有与s
相同的底层数组的值。
Instantiations 实例化
A generic function or type is instantiated by substituting type arguments for the type parameters [Go 1.18]. Instantiation proceeds in two steps:
泛型函数
或泛型
是通过用类型实参
替换类型参数
而被实例化的。实例化分两步进行:
- 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.
- 在泛型声明中,每个类型参数都被替换为其对应的类型实参。这种替换发生在整个函数或类型声明中,包括类型参数列表本身和该列表中的每个类型。
- 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.
实例化一个(泛型)类型会生成一个新的非泛型的命名类型;实例化一个(泛型)函数会产生一个新的非泛型的函数。
|
|
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.
在所有其他情况下,必须存在(可能部分)类型实参列表。如果类型实参列表缺失或部分,则所有缺失的类型实参都必须可以从函数使用的上下文中推断出来。
|
|
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”.
(泛型函数的)部分类型实参列表不能是空的;至少第一个(类型)实参必须存在。该列表是完整的类型实参列表的前缀,剩下的实参需要被推断。宽泛地说,类型实参可以从 “从右到左"省略。
|
|
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 inference uses the type relationships between pairs of types for inference: For instance, a function argument must be assignable to its respective function parameter; this establishes a relationship between the type of the argument and the type of the parameter. If either of these two types contains type parameters, type inference looks for the type arguments to substitute the type parameters with such that the assignability relationship is satisfied. Similarly, type inference uses the fact that a type argument must satisfy the constraint of its respective type parameter.
类型推断使用类型对之间的类型关系进行推断:例如,函数实参必须可赋值给其各自的函数参数;这在实参类型和参数类型之间建立了关系。如果这两个类型中的任何一个都包含类型参数,则类型推断会查找类型实参以用满足可赋值关系的类型参数替换它们。类似地,类型推断使用类型实参必须满足其各自类型参数的约束这一事实。
Each such pair of matched types corresponds to a type equation containing one or multiple type parameters, from one or possibly multiple generic functions. Inferring the missing type arguments means solving the resulting set of type equations for the respective type parameters.
每一对匹配的类型对应一个类型方程,其中包含一个或多个类型参数,来自一个或多个泛型函数。推断缺失的类型参数意味着为各个类型参数求解所得的类型方程组。
For example, given
例如,给定
// dedup returns a copy of the argument slice with any duplicate entries removed.
func dedup[S ~[]E, E comparable](S) S { … }
type Slice []int
var s Slice
s = dedup(s) // same as s = dedup[Slice, int](s)
the variable s
of type Slice
must be assignable to the function parameter type S
for the program to be valid. To reduce complexity, type inference ignores the directionality of assignments, so the type relationship between Slice
and S
can be expressed via the (symmetric) type equation Slice ≡A S
(or S ≡A Slice
for that matter), where the A
in ≡A
indicates that the LHS and RHS types must match per assignability rules (see the section on type unification for details). Similarly, the type parameter S
must satisfy its constraint ~[]E
. This can be expressed as S ≡C ~[]E
where X ≡C Y
stands for “X
satisfies constraint Y
”. These observations lead to a set of two equations
变量 s
的类型 Slice
必须可赋值给函数参数类型 S
,程序才有效。为了降低复杂性,类型推断忽略了赋值的方向性,因此 Slice
和 S
之间的关系可以通过(对称)类型方程 Slice ≡A S
(或者 S ≡A Slice
)来表示,其中 ≡A
中的 A
表示 LHS 和 RHS 类型必须根据可赋值性规则匹配(有关详细信息,请参阅类型统一部分)。同样,类型参数 S
必须满足其约束 ~[]E
。这可以表示为 S ≡C ~[]E
,其中 X ≡C Y
表示“ X
满足约束 Y
”。这些观察结果产生了一组两个方程
Slice ≡A S (1)
S ≡C ~[]E (2)
which now can be solved for the type parameters S
and E
. From (1) a compiler can infer that the type argument for S
is Slice
. Similarly, because the underlying type of Slice
is []int
and []int
must match []E
of the constraint, a compiler can infer that E
must be int
. Thus, for these two equations, type inference infers
现在可以为类型参数 S
和 E
求解。从 (1) 中,编译器可以推断出 S
的类型参数为 Slice
。类似地,因为 Slice
的底层类型为 []int
,并且 []int
必须匹配约束的 []E
,所以编译器可以推断出 E
必须为 int
。因此,对于这两个方程,类型推断推断出
S ➞ Slice
E ➞ int
Given a set of type equations, the type parameters to solve for are the type parameters of the functions that need to be instantiated and for which no explicit type arguments is provided. These type parameters are called bound type parameters. For instance, in the dedup
example above, the type parameters S
and E
are bound to dedup
. An argument to a generic function call may be a generic function itself. The type parameters of that function are included in the set of bound type parameters. The types of function arguments may contain type parameters from other functions (such as a generic function enclosing a function call). Those type parameters may also appear in type equations but they are not bound in that context. Type equations are always solved for the bound type parameters only.
给定一组类型方程,要解决的类型参数是需要实例化的函数的类型参数,并且没有为此提供显式类型参数。这些类型参数称为绑定类型参数。例如,在上面的 dedup
示例中,类型参数 S
和 E
绑定到 dedup
。对泛型函数调用的参数本身可能是一个泛型函数。该函数的类型参数包含在绑定类型参数集中。函数参数的类型可能包含来自其他函数的类型参数(例如包含函数调用的泛型函数)。这些类型参数也可能出现在类型方程中,但它们在此上下文中没有绑定。类型方程始终仅针对绑定类型参数求解。
Type inference supports calls of generic functions and assignments of generic functions to (explicitly function-typed) variables. This includes passing generic functions as arguments to other (possibly also generic) functions, and returning generic functions as results. Type inference operates on a set of equations specific to each of these cases. The equations are as follows (type argument lists are omitted for clarity):
类型推断支持调用泛型函数并将泛型函数赋值给(显式函数类型的)变量。这包括将泛型函数作为参数传递给其他(可能也是泛型)函数,并将泛型函数作为结果返回。类型推断针对每种情况的一组特定方程进行操作。方程式如下(为清楚起见,省略了类型参数列表):
For a function call
f(a0, a1, …)
wheref
or a function argumentai
is a generic function:对于函数调用
f(a0, a1, …)
,其中f
或函数参数ai
是泛型函数: Each pair(ai, pi)
of corresponding function arguments and parameters whereai
is not an untyped constant yields an equationtypeof(pi) ≡A typeof(ai)
.每个函数实参和参数的对应对
(ai, pi)
(其中ai
不是未类型化的常量)生成一个方程typeof(pi) ≡A typeof(ai)
。 Ifai
is an untyped constantcj
, andtypeof(pi)
is a bound type parameterPk
, the pair(cj, Pk)
is collected separately from the type equations.如果
ai
是未类型化的常量cj
,并且typeof(pi)
是绑定类型参数Pk
,则对(cj, Pk)
从类型方程中单独收集。For an assignment
v = f
of a generic functionf
to a (non-generic) variablev
of function type:对于将泛型函数
f
赋值给函数类型(非泛型)变量v
:typeof(v) ≡A typeof(f)
.For a return statement
return …, f, …
wheref
is a generic function returned as a result to a (non-generic) result variabler
of function type:对于返回语句
return …, f, …
(其中f
是作为结果返回给函数类型(非泛型)结果变量r
的泛型函数):typeof(r) ≡A typeof(f)
.
Additionally, each type parameter Pk
and corresponding type constraint Ck
yields the type equation Pk ≡C Ck
.
此外,每个类型参数 Pk
和相应的类型约束 Ck
生成类型方程 Pk ≡C Ck
。
Type inference gives precedence to type information obtained from typed operands before considering untyped constants. Therefore, inference proceeds in two phases:
类型推断优先考虑从类型化操作数获得的类型信息,然后再考虑非类型化常量。因此,推断分两个阶段进行:
- The type equations are solved for the bound type parameters using type unification. If unification fails, type inference fails.
- 使用类型统一来解决绑定类型参数的类型方程。如果统一失败,则类型推断失败。
- For each bound type parameter
Pk
for which no type argument has been inferred yet and for which one or more pairs(cj, Pk)
with that same type parameter were collected, determine the constant kind of the constantscj
in all those pairs the same way as for constant expressions. The type argument forPk
is the default type for the determined constant kind. If a constant kind cannot be determined due to conflicting constant kinds, type inference fails. - 对于尚未推断出类型参数的每个绑定类型参数
Pk
,以及为此收集了一个或多个具有相同类型参数的配对(cj, Pk)
,以与常量表达式相同的方式确定所有这些配对中常量cj
的常量类型。Pk
的类型参数是确定常量类型的默认类型。如果由于常量类型冲突而无法确定常量类型,则类型推断失败。
If not all type arguments have been found after these two phases, type inference fails.
如果在完成这两个阶段后仍未找到所有类型实参,则类型推断失败。
If the two phases are successful, type inference determined a type argument for each bound type parameter:
如果这两个阶段都成功,类型推断将为每个受限类型参数确定一个类型参数:
Pk ➞ Ak
A type argument Ak
may be a composite type, containing other bound type parameters Pk
as element types (or even be just another bound type parameter). In a process of repeated simplification, the bound type parameters in each type argument are substituted with the respective type arguments for those type parameters until each type argument is free of bound type parameters.
类型实参 Ak
可以是复合类型,包含其他绑定类型参数 Pk
作为元素类型(甚至可以只是另一个绑定类型参数)。在重复简化的过程中,每个类型参数中的绑定类型参数都用这些类型参数的相应类型参数替换,直到每个类型参数都不包含绑定类型参数。
If type arguments contain cyclic references to themselves through bound type parameters, simplification and thus type inference fails. Otherwise, type inference succeeds.
如果类型实参通过绑定类型参数包含对自身的循环引用,则简化和类型推断将失败。否则,类型推断将成功。
Type unification 类型联合
Type inference solves type equations through type unification. Type unification recursively compares the LHS and RHS types of an equation, where either or both types may be or contain bound type parameters, and looks for type arguments for those type parameters such that the LHS and RHS match (become identical or assignment-compatible, depending on context). To that effect, type inference maintains a map of bound type parameters to inferred type arguments; this map is consulted and updated during type unification. Initially, the bound type parameters are known but the map is empty. During type unification, if a new type argument A
is inferred, the respective mapping P ➞ A
from type parameter to argument is added to the map. Conversely, when comparing types, a known type argument (a type argument for which a map entry already exists) takes the place of its corresponding type parameter. As type inference progresses, the map is populated more and more until all equations have been considered, or until unification fails. Type inference succeeds if no unification step fails and the map has an entry for each type parameter.
类型推断通过类型统一来解决类型方程。类型统一递归地比较方程的 LHS 和 RHS 类型,其中任一类型或同时两种类型可能为绑定类型参数或包含绑定类型参数,并查找这些类型参数的类型参数,以便 LHS 和 RHS 匹配(根据上下文,变为相同或可赋值兼容)。为此,类型推断维护一个从绑定类型参数到推断类型参数的映射;在类型统一期间查阅并更新此映射。最初,绑定类型参数是已知的,但映射是空的。在类型统一期间,如果推断出新的类型参数 A
,则将从类型参数到参数的相应映射 P ➞ A
添加到映射中。相反,在比较类型时,已知类型参数(已存在映射项的类型参数)取代其对应的类型参数。随着类型推断的进行,映射会逐渐填充,直到考虑所有方程或直到统一失败。如果没有任何统一步骤失败并且映射为每个类型参数都有一个条目,则类型推断成功。
For example, given the type equation with the bound type parameter P
例如,给定具有绑定类型参数 P
的类型方程
[10]struct{ elem P, list []P } ≡A [10]struct{ elem string; list []string }
type inference starts with an empty map. Unification first compares the top-level structure of the LHS and RHS types. Both are arrays of the same length; they unify if the element types unify. Both element types are structs; they unify if they have the same number of fields with the same names and if the field types unify. The type argument for P
is not known yet (there is no map entry), so unifying P
with string
adds the mapping P ➞ string
to the map. Unifying the types of the list
field requires unifying []P
and []string
and thus P
and string
. Since the type argument for P
is known at this point (there is a map entry for P
), its type argument string
takes the place of P
. And since string
is identical to string
, this unification step succeeds as well. Unification of the LHS and RHS of the equation is now finished. Type inference succeeds because there is only one type equation, no unification step failed, and the map is fully populated.
类型推断从一个空映射开始。统一首先比较 LHS 和 RHS 类型的顶级结构。两者都是长度相同的数组;如果元素类型统一,它们就会统一。两种元素类型都是结构;如果它们具有相同数量的具有相同名称的字段,并且字段类型统一,则它们会统一。对于 P
的类型参数尚不知道(没有映射条目),因此将 P
与 string
统一会将映射 P ➞ string
添加到映射中。统一 list
字段的类型需要统一 []P
和 []string
,从而统一 P
和 string
。由于此时已知 P
的类型参数( P
有一个映射条目),因此其类型参数 string
取代了 P
。并且由于 string
与 string
相同,因此此统一步骤也成功。现在,方程的 LHS 和 RHS 的统一已完成。类型推断成功,因为只有一个类型方程,没有统一步骤失败,并且映射已完全填充。
Unification uses a combination of exact and loose unification depending on whether two types have to be identical, assignment-compatible, or only structurally equal. The respective type unification rules are spelled out in detail in the Appendix.
统一使用精确和松散统一的组合,具体取决于两种类型是否必须相同、是否兼容赋值或仅在结构上相等。相应的类型统一规则在附录中详细说明。
For an equation of the form X ≡A Y
, where X
and Y
are types involved in an assignment (including parameter passing and return statements), the top-level type structures may unify loosely but element types must unify exactly, matching the rules for assignments.
对于形式为 X ≡A Y
的方程,其中 X
和 Y
是参与赋值的类型(包括参数传递和返回语句),顶级类型结构可能松散地统一,但元素类型必须完全统一,与赋值规则相匹配。
For an equation of the form P ≡C C
, where P
is a type parameter and C
its corresponding constraint, the unification rules are bit more complicated:
对于形式为 P ≡C C
的方程,其中 P
是类型参数, C
是其对应的约束,统一规则要复杂一些:
- If
C
has a core typecore(C)
andP
has a known type argumentA
,core(C)
andA
must unify loosely. IfP
does not have a known type argument andC
contains exactly one type termT
that is not an underlying (tilde) type, unification adds the mappingP ➞ T
to the map. - 如果
C
具有核心类型core(C)
,P
具有已知类型参数A
,则core(C)
和A
必须松散地统一。如果P
没有已知类型参数,并且C
恰好包含一个不是底层(波浪号)类型的类型项T
,则统一会将映射P ➞ T
添加到映射中。 - If
C
does not have a core type andP
has a known type argumentA
,A
must have all methods ofC
, if any, and corresponding method types must unify exactly. - 如果
C
没有核心类型,P
具有已知类型参数A
,则A
必须具有C
的所有方法(如果有),并且相应的方法类型必须完全统一。
When solving type equations from type constraints, solving one equation may infer additional type arguments, which in turn may enable solving other equations that depend on those type arguments. Type inference repeats type unification as long as new type arguments are inferred.
在从类型约束中求解类型方程时,求解一个方程可能会推断出其他类型参数,进而可能能够求解依赖于这些类型参数的其他方程。只要推断出新的类型参数,类型推断就会重复类型统一。
Operators 操作符
Operators combine operands into expressions.
操作符将操作数组合成表达式。
Expression = UnaryExpr | Expression binary_op Expression .
UnaryExpr = PrimaryExpr | unary_op UnaryExpr .
binary_op = "||" | "&&" | rel_op | add_op | mul_op .
rel_op = "==" | "!=" | "<" | "<=" | ">" | ">=" .
add_op = "+" | "-" | "|" | "^" .
mul_op = "*" | "/" | "%" | "<<" | ">>" | "&" | "&^" .
unary_op = "+" | "-" | "!" | "^" | "*" | "&" | "<-" .
Comparisons are discussed elsewhere. For other binary operators, the operand types must be identical unless the operation involves shifts or untyped constants. For operations involving constants only, see the section on constant expressions.
比较操作符将在其他地方讨论。对于其他二元运算符,除非操作涉及移位或无类型的常量,操作数类型必须是一致的。对于只涉及常量的操作,请参见常量表达式部分。
Except for shift operations, if one operand is an untyped constant and the other operand is not, the constant is implicitly converted to the type of the other operand.
除了移位操作符之外,如果一个操作数是无类型
常量,而另一个操作数不是,那么该常量将被隐式地转换为另一个操作数的类型。
The right operand in a shift expression must have integer type [Go 1.13] or be an untyped constant representable by a value of type uint
. If the left operand of a non-constant shift expression is an untyped constant, it is first implicitly converted to the type it would assume if the shift expression were replaced by its left operand alone.
移位表达式中的右操作数
必须是整数类型,或者是可以用uint
类型的值表示的无类型
常量。如果一个非常量移位表达式的左操作数
是一个无类型
常量,那么它首先被隐式地转换为假设移位表达式被其左操作数单独替换时的类型。
|
|
Operator precedence 优先级运算符
Unary operators have the highest precedence. As the ++
and --
operators form statements, not expressions, they fall outside the operator hierarchy. As a consequence, statement *p++
is the same as (*p)++
.
一元运算符的优先级最高。由于++
和--
运算符构成的是语句,而不是表达式,因此它们不属于运算符等级体系。因此,语句*p++
与(*p)++
相同。
There are five precedence levels for binary operators. Multiplication operators bind strongest, followed by addition operators, comparison operators, &&
(logical AND), and finally ||
(logical OR):
二元运算符有五个优先级别。乘法运算符
绑定最强,其次是加法运算符
、比较运算符、&&
(逻辑AND),最后是||
(逻辑OR)。
|
|
Binary operators of the same precedence associate from left to right. For instance, x / y * z
is the same as (x / y) * z
.
优先级相同的二元运算符按从左到右的顺序结合。例如,x / y * z
等同于(x / y)* z
。
|
|
Arithmetic operators 算术运算符
Arithmetic operators apply to numeric values and yield a result of the same type as the first operand. The four standard arithmetic operators (+
, -
, *
, /
) apply to integer, floating-point, and complex types; +
also applies to strings. The bitwise logical and shift operators apply to integers only.
算术运算符适用于数字值,产生的结果与第一个操作数的类型
相同。四个标准的算术运算符(+
、-
、*
、/
)适用于整型、浮点型和复数型;+
也适用于字符串。位逻辑运算符和移位运算符只适用于整型。
|
|
If the operand type is a type parameter, the operator must apply to each type in that type set. The operands are represented as values of the type argument that the type parameter is instantiated with, and the operation is computed with the precision of that type argument. For example, given the function:
如果操作数类型是类型参数,那么操作数必须适用于该类型集中的每个类型。操作数被表示为类型参数被实例化的类型实参的值,并且操作以该类型实参的精度进行计算。例如,给定一个函数:
|
|
the product x * y
and the addition s += x * y
are computed with float32
or float64
precision, respectively, depending on the type argument for F
.
x * y
的乘积 和 s += x * y
的加法分别以float32
或float64
精度计算,这取决于F
的类型实参。
Integer operators 整数运算符
For two integer values x
and y
, the integer quotient q = x / y
and remainder r = x % y
satisfy the following relationships:
对于两个整数值x
和y
,它们的整数商q = x / y
和余数r = x % y
满足以下关系:
x = q*y + r and |r| < |y|
with x / y
truncated towards zero (“truncated division”).
with x / y
被截断到零("截断除法")。
|
|
The one exception to this rule is that if the dividend x
is the most negative value for the int type of x
, the quotient q = x / -1
is equal to x
(and r = 0
) due to two’s-complement integer overflow:
这条规则有一个例外:如果)被除数
(dividendx
是x
的int
类型的最负值,那么商q = x / -1
就等于x
(而r = 0
),这是由于二元补码的整数溢出:
|
|
If the divisor is a constant, it must not be zero. If the divisor is zero at run time, a run-time panic occurs. If the dividend is non-negative and the divisor is a constant power of 2, the division may be replaced by a right shift, and computing the remainder may be replaced by a bitwise AND operation:
如果除数
(divisor)是一个常量,那么它一定不能为零。如果除数在运行时为零,就会发生运行时恐慌。如果除数是非负数,并且除数是2的常数幂,除法可以用右移
来代替,计算余数可以用按位与
操作来代替。
|
|
The shift operators shift the left operand by the shift count specified by the right operand, which must be non-negative. If the shift count is negative at run time, a run-time panic occurs. The shift operators implement arithmetic shifts if the left operand is a signed integer and logical shifts if it is an unsigned integer. There is no upper limit on the shift count. Shifts behave as if the left operand is shifted n
times by 1 for a shift count of n
. As a result, x << 1
is the same as x*2
and x >> 1
is the same as x/2
but truncated towards negative infinity.
移位运算符通过右操作数指定的移位计数
对左操作数进行移位,移位计数
必须为非负数。如果移位计数
在运行时为负数,就会发生运行时恐慌。如果左操作数
是有符号整数
,移位操作符实现算术移位
;如果是无符号整数
,则实现逻辑移位
。移位计数
没有上限。移位计数
为n
的移位行为就像左操作数被1
移了n
次。因此,x<<1
与x*2
相同,x>>1
与x/2
相同(但向右移位被截断到负无穷大)。
For integer operands, the unary operators +
, -
, and ^
are defined as follows:
对于整数操作数,一元运算符+
、-
和^
的定义如下:
|
|
Integer overflow 整数溢出
For unsigned integer values, the operations +
, -
, *
, and <<
are computed modulo \(2^n\), where n is the bit width of the unsigned integer’s type. Loosely speaking, these unsigned integer operations discard high bits upon overflow, and programs may rely on “wrap around”.
对于无符号整型值,+
、-
、*
和<<
运算是以 \(2^n\)为模来计算的,其中n
是无符号整型的位宽。广义上讲,这些无符号整型操作在溢出时丢弃高位
,程序可以依靠 “wrap around
"。
For signed integers, the operations +
, -
, *
, /
, and <<
may legally overflow and the resulting value exists and is deterministically defined by the signed integer representation, the operation, and its operands. Overflow does not cause a run-time panic. A compiler may not optimize code under the assumption that overflow does not occur. For instance, it may not assume that x < x + 1
is always true.
对于有符号整型值
,+
、-
、*
、/
和<<
运算可以合法地溢出
,其产生的值是存在的,并且可以被有符号整型表示法、其操作和操作数明确地定义。溢出不会引起运行时恐慌。在假设不发生溢出的情况下,编译器可能不会优化代码。例如,它不会假设x<x+1
总是真的。
Floating-point operators 浮点运算符
For floating-point and complex numbers, +x
is the same as x
, while -x
is the negation of x
. The result of a floating-point or complex division by zero is not specified beyond the IEEE-754 standard; whether a run-time panic occurs is implementation-specific.
对于浮点数和复数,+x
与x
相同,而-x
是负的x
。浮点数或复数除以0的结果,在IEEE-754标准中没有规定;是否会发生运行时恐慌是由具体实现决定的。
An implementation may combine multiple floating-point operations into a single fused operation, possibly across statements, and produce a result that differs from the value obtained by executing and rounding the instructions individually. An explicit floating-point type conversion rounds to the precision of the target type, preventing fusion that would discard that rounding.
某些实现可能会将多个浮点运算合并为一个单一的融合运算,可能会跨越语句,产生的结果与单独执行和舍入指令得到的值不同。显式的浮点类型转换是按照目标类型的精度进行舍入的,这样就可以避免融合时放弃舍入的做法。=> 仍有疑问??
For instance, some architectures provide a “fused multiply and add” (FMA) instruction that computes x*y + z
without rounding the intermediate result x*y
. These examples show when a Go implementation can use that instruction:
例如,一些体系架构提供了一个 “fused multiply and add
"(FMA
)指令,其在计算x*y+z
时,不对中间结果x*y
进行舍入。这些例子显示了Go的实现何时可以使用该指令:
|
|
String concatenation 字符串连接
Strings can be concatenated using the +
operator or the +=
assignment operator:
字符串可以使用+
运算符或+=
赋值运算符进行连接:
|
|
String addition creates a new string by concatenating the operands.
字符串加法通过连接操作数创建一个新的字符串。
Comparison operators 比较运算符
Comparison operators compare two operands and yield an untyped boolean value.
比较运算符比较两个操作数,并产生一个无类型布尔值。
== equal
!= not equal
< less
<= less or equal
> greater
>= greater or equal
In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.
在任何比较中,第一个操作数必须是可分配给第二个操作数的类型,反之亦然。
The equality operators ==
and !=
apply to operands of comparable types. The ordering operators <
, <=
, >
, and >=
apply to operands of ordered types. These terms and the result of the comparisons are defined as follows:
相等运算符==
和!=
适用于可比较的操作数。排序运算符<
, <=
, >
, 和>=
适用于被排序的操作数。这些术语和比较结果的定义如下:
- Boolean types are comparable. Two boolean values are equal if they are either both
true
or bothfalse
. - 布尔值是可比较的。如果两个布尔值都是
true
或者都是false
,那么它们是相等的。 - Integer types are comparable and ordered. Two integer values are compared in the usual way.
- 按照通常的方式,整数值是可比较的并且是可排序的。
- Floating-point types are comparable and ordered. Two floating-point values are compared as defined by the IEEE-754 standard.
- 按照IEEE-754标准的定义,浮点值是可比较的并且是可排序的。
- Complex types are comparable. Two complex values
u
andv
are equal if bothreal(u) == real(v)
andimag(u) == imag(v)
. - 复数值是可比较的。如果
real(u) == real(v)
和imag(u) == imag(v)
,则这两个复数值u
和v
是相等的。 - String types are comparable and ordered. Two string values are compared lexically byte-wise.
- 字符串类型值是可比较和有序的。两个字符串值按字节顺序进行比较。
- Pointer types are comparable. Two pointer values are equal if they point to the same variable or if both have value
nil
. Pointers to distinct zero-size variables may or may not be equal. - 指针值是可比较的。如果两个指针值指向同一个变量,或者两个指针值都是
nil
,则它们的值是相等的。指向不同的零尺寸变量的指针值可能相等,也可能不相等。 - Channel types are comparable. Two channel values are equal if they were created by the same call to
make
or if both have valuenil
. - 通道值是可比较的。如果两个通道值是由相同的 make调用创建的,或者它们的值都为
nil
,则它们的值是相等的。 - 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
. - 接口值是可比较的。如果两个接口值有一致的动态类型和相同的动态值,或者两者的值都是
nil
,则它们的值是相等的。 - A value
x
of non-interface typeX
and a valuet
of interface typeT
can be compared if typeX
is comparable andX
implementsT
. They are equal ift
’s dynamic type is identical toX
andt
’s dynamic value is equal tox
. - 非接口类型
X
的值x
和接口类型T
的值t
,在X
类型的值是可比较的并且X
实现T
时是可比较的。如果t
的动态类型等于X
,且t
的动态值等于x
,则它们是相等的。 - Struct types are comparable if all their field types are comparable. Two struct values are equal if their corresponding non-blank field values are equal. The fields are compared in source order, and comparison stops as soon as two field values differ (or all fields have been compared).
- 如果结构体类型的所有字段类型都是可比较的,那么结构体类型的值是可比较的。如果两个结构体对应的非空白字段值相等,那么这两个结构体值相等。字段按源代码顺序进行比较,并在两个字段值不同时停止比较(或已比较完所有字段)。
- Array types are comparable if their array element types are comparable. Two array values are equal if their corresponding element values are equal. The elements are compared in ascending index order, and comparison stops as soon as two element values differ (or all elements have been compared).
- 如果它们的数组元素类型是可比较的,那么数组类型的值是可比较的。如果它们对应的元素值相等,两个数组值相等。元素按升序索引进行比较,并在两个元素值不同时停止比较(或已比较完所有元素)。
- Type parameters are comparable if they are strictly comparable (see below).
- 如果类型参数是严格可比较的(见下文),则类型参数的值是可比较的。
A comparison of two interface values with identical dynamic types causes a run-time panic if that type is not comparable. This behavior applies not only to direct interface value comparisons but also when comparing arrays of interface values or structs with interface-valued fields.
对两个动态类型相同的接口值进行比较,如果它们的类型值不具有可比性,则会引起运行时恐慌。这种行为不仅适用于直接的接口值比较,也适用于比较接口值的数组或带有接口类型字段的结构体。
Slice, map, and function types are not comparable. However, as a special case, a slice, map, or function value may be compared to the predeclared identifier nil
. Comparison of pointer, channel, and interface values to nil
is also allowed and follows from the general rules above.
切片值、映射值和函数值是不可比较的
。然而,作为一种特殊情况,切片值、映射值或函数值可以与预先声明的标识符nil
比较。指针值、通道值和接口值与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.
Logical operators 逻辑运算符
Logical operators apply to boolean values and yield a result of the same type as the operands. The left operand is evaluated, and then the right if the condition requires it.
逻辑运算符适用于布尔值,并产生一个与操作数相同类型的结果。右操作数是按条件进行求值的。
|
|
Address operators 地址运算符
For an operand x
of type T
, the address operation &x
generates a pointer of type *T
to x
. The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array. As an exception to the addressability requirement, x
may also be a (possibly parenthesized) composite literal. If the evaluation of x
would cause a run-time panic, then the evaluation of &x
does too.
对于类型为T
的操作数x
,寻址操作&x
生成一个类型为*T
的指针指向x
。该操作数x
必须是可寻址的,也就是说,它要么是一个变量、指针间接引用(pointer indirection)或对切片的索引操作(slice indexing operation,是一个名词)
;要么是一个可寻址结构体操作数的字段选择器;要么是一个可寻址数组的数组索引操作。作为可寻址要求的一个例外,x
也可以是一个(可能带括号的)复合字面量。如果对x
的求值会引起运行时恐慌,那么对&x
的求值也会引起运行时恐慌。
For an operand x
of pointer type *T
, the pointer indirection *x
denotes the variable of type T
pointed to by x
. If x
is nil
, an attempt to evaluate *x
will cause a run-time panic.
对于指针类型*T
的操作数x
,指针间接引用*x
表示指向x
的类型T
的变量,如果x
是nil
,试图求值*x
将导致运行时恐慌。
|
|
Receive operator 接收操作符
For an operand ch
whose core type is a channel, the value of the receive operation <-ch
is the value received from the channel ch
. The channel direction must permit receive operations, and the type of the receive operation is the element type of the channel. The expression blocks until a value is available. Receiving from a nil
channel blocks forever. A receive operation on a closed channel can always proceed immediately, yielding the element type’s zero value after any previously sent values have been received.
对于核心类型为通道的操作数ch
,接收操作<-ch
的值是从通道ch
中接收的值,通道方向必须允许接收操作,接收操作的类型是通道的元素类型。这个表达式会阻塞,直到有一个可用的值。从一个 nil
的通道接收时,将永远阻塞。在一个已经关闭的通道上的接收操作总是可以立即进行,并在任何先前发送的值被接收后,产生一个该元素类型的零值。
|
|
A receive expression used in an assignment statement or initialization of the special form
在赋值语句或特殊形式的初始化中使用的一个接收表达式
|
|
yields an additional untyped boolean result reporting whether the communication succeeded. The value of ok
is true
if the value received was delivered by a successful send operation to the channel, or false
if it is a zero value generated because the channel is closed and empty.
将产生一个额外的无类型布尔值结果,报告通信是否成功。如果收到的值是由一个成功的发送操作传递给通道的,那么ok
的值为true
,如果通道已关闭且为空,生成的值为零值,则 ok
的值为 false
。
Conversions 转换
A conversion changes the type of an expression to the type specified by the conversion. A conversion may appear literally in the source, or it may be implied by the context in which an expression appears.
转换将表达式的类型改变为转换所指定的类型。转换可以出现在源文件中的字面量上,也可以隐含在由表达式所在的上下文。
An explicit conversion is an expression of the form T(x)
where T
is a type and x
is an expression that can be converted to type T
.
式转换是形如 T(x)
的表达式,其中T
是一个类型,x
是可以被转换为T
类型的表达式。
Conversion = Type "(" Expression [ "," ] ")" .
If the type starts with the operator *
or <-
, or if the type starts with the keyword func
and has no result list, it must be parenthesized when necessary to avoid ambiguity:
如果类型以运算符*
或<-
开头,或者如果类型以关键字func
开头,并且没有结果列表,那么在必要时必须用圆括号
括起来,以避免产生歧义:
|
|
A constant value x
can be converted to type T
if x
is representable by a value of T
. As a special case, an integer constant x
can be explicitly converted to a string type using the same rule as for non-constant x
.
一个常量值x
可以被转换为T
类型,如果x
可以用T
的一个值来表示的话。作为一种特殊情况,可以使用 与 非常量x
相同的规则显式地将整数常量x
转换为字符串类型。
Converting a constant to a type that is not a type parameter yields a typed constant.
将常量转换为非类型参数的类型,会生成一个有类型的常量。
|
|
Converting a constant to a type parameter yields a non-constant value of that type, with the value represented as a value of the type argument that the type parameter is instantiated with. For example, given the function:
将常量转换为一个类型参数会生成一个该类型的非常量值(non-constant value),该值表示为类型参数实例化时所带的类型实参的值。例如,给定一个(泛型)函数:
|
|
the conversion P(1.1)
results in a non-constant value of type P
and the value 1.1
is represented as a float32
or a float64
depending on the type argument for f
. Accordingly, if f
is instantiated with a float32
type, the numeric value of the expression P(1.1) + 1.2
will be computed with the same precision as the corresponding non-constant float32
addition.
转换P(1.1)
的结果是一个P
类型的非常量值(non-constant value),而值1.1
被表示为float32
或float64
,这取决于f
的类型参数。因此,如果f
被实例化为float32
类型,那么表达式P(1.1)+1.2
的数值会用与非常量float32
加法相同的精度进行计算。
A non-constant value x
can be converted to type T
in any of these cases:
在以下情况下,非常量值 x
可以转换为类型 T
:
x
is assignable toT
.x
可以被分配给T
。- ignoring struct tags (see below),
x
’s type andT
are not type parameters but have identical underlying types. - 忽略结构体标签(见下文),
x
的类型和T
不是类型参数,但有一致的底层类型。 - ignoring struct tags (see below),
x
’s type andT
are pointer types that are not named types, and their pointer base types are not type parameters but have identical underlying types. - 忽略结构体标签(见下文),
x
的类型和T
都是指针类型,且它们不是命名类型,它们的指针基类型不是类型参数,但有一致的底层类型。 x
’s type andT
are both integer or floating point types.x
的类型和T
都是整型或浮点型。x
’s type andT
are both complex types.x
的类型和T
都是复数类型。x
is an integer or a slice of bytes or runes andT
is a string type.x
是一个整型、字节型、符文型的切片,T
是一个字符串类型。x
is a string andT
is a slice of bytes or runes.x
是一个字符串类型,T
是一个字节型、符文型的切片。x
is a slice,T
is an array [Go 1.20] or a pointer to an array [Go 1.17], and the slice and array types have identical element types.x
是切片,T
是数组[Go 1.20] 或数组指针[Go 1.17],切片和数组类型具有一致的元素类型。
Additionally, if T
or x
’s type V
are type parameters, x
can also be converted to type T
if one of the following conditions applies:
此外,如果T
或x
的类型V
是类型参数,如果满足以下条件之一,x
也可以被转换为T
类型:
- Both
V
andT
are type parameters and a value of each type inV
’s type set can be converted to each type inT
’s type set. V
和T
都是类型参数,并且V
的类型集中的每个类型的值都可以转换为T
的类型集中的每个类型。- Only
V
is a type parameter and a value of each type inV
’s type set can be converted toT
. - 只有
V
是一个类型参数,并且V
的类型集中的每个类型的值都可以转换为T
。 - Only
T
is a type parameter andx
can be converted to each type inT
’s type set. - 只有
T
是一个类型参数,并且x
可以转换为T
的类型集中的每个类型。
Struct tags are ignored when comparing struct types for identity for the purpose of conversion:
为了转换的目的,在比较结构体类型的是否一致时,结构体标签被忽略:
|
|
Specific rules apply to (non-constant) conversions between numeric types or to and from a string type. These conversions may change the representation of x
and incur a run-time cost. All other conversions only change the type but not the representation of x
.
数值类型之间或与字符串类型之间的(非常量)转换有特殊的规则。这些转换可能会改变x
的表示,并产生运行时开销。而所有其他的转换只改变x
的类型而不改变其表示。
There is no linguistic mechanism to convert between pointers and integers. The package unsafe
implements this functionality under restricted circumstances.
在指针和整数之间没有语言机制可以直接进行转换。unsafe包在受限制的情况下实现了这个功能。
Conversions between numeric types 数值型之间的转换
For the conversion of non-constant numeric values, the following rules apply:
对于非常量数值的转换,有以下特定规则:
When converting between integer types, if the value is a signed integer, it is sign extended to implicit infinite precision; otherwise it is zero extended. It is then truncated to fit in the result type’s size. For example, if
v := uint16(0x10F0)
, thenuint32(int8(v)) == 0xFFFFFFF0
. The conversion always yields a valid value; there is no indication of overflow.当在整型之间转换时,如果数值是有符号的整型,则进行符号扩展以达到隐式的无限精度;否则它被零扩展。然后,它被截断以适应结果类型的大小。例如,如果
v := uint16(0x10F0)
,那么uint32(int8(v)) == 0xFFFFFFF0
。该转换总是产生一个有效的值;没有溢出的迹象。个人注释
1 2 3 4 5 6 7 8 9 10
package main import "fmt" func main() { v := uint16(0x10F0) fmt.Printf("%#X\n", v) // 0X10F0 fmt.Printf("%#X\n", int8(v)) // -0X10 fmt.Printf("%#X\n", uint32(int8(v))) // 0XFFFFFFF0 }
When converting a floating-point number to an integer, the fraction is discarded (truncation towards zero).
当把浮点型数值转换为整型时,小数会被丢弃(向零截断)。
个人注释
1 2 3 4 5 6 7 8 9
package main import "fmt" func main() { v := 1.23 fmt.Printf("%v\n", v) // 1.23 fmt.Printf("%v\n", int8(v)) // 1 }
When converting an integer or floating-point number to a floating-point type, or a complex number to another complex type, the result value is rounded to the precision specified by the destination type. For instance, the value of a variable
x
of typefloat32
may be stored using additional precision beyond that of an IEEE-754 32-bit number, but float32(x) represents the result of roundingx
’s value to 32-bit precision. Similarly,x + 0.1
may use more than 32 bits of precision, butfloat32(x + 0.1)
does not.当将一个整型或浮点型数值转换为浮点型,或将一个复数型数值转换为另一个复数类型时,结果值被舍入到目标类型所指定的精度。例如,
float32
类型的变量x
的值可能会使用超出IEEE-754 32位数的额外精度来存储,但是float32(x)
表示将x
的值舍入到32
位精度的结果。同样地,x + 0.1
可能使用超过32
位的精度,但是float32(x + 0.1)
则不会。个人注释
请给出如何将一个复数型数值转换为另一个复数类型的示例:
1 2 3 4 5 6 7 8 9 10
package main import "fmt" func main() { c1 := complex(1.2, 3.0) c2 := complex(float32(real(c1)), float32(imag(c1))) fmt.Printf("%T,%v\n", c1, c1) // complex128,(1.2+3i) fmt.Printf("%T,%v\n", c2, c2) // complex64,(1.2+3i) }
如何使用
math.big
来解决浮点数计算问题?TODO
In all non-constant conversions involving floating-point or complex values, if the result type cannot represent the value the conversion succeeds but the result value is implementation-dependent.
在所有涉及浮点值或复数值的非常量转换中,如果结果类型不能表示该值,转换仍会成功,但结果值取决于实现。
Conversions to and from a string type 与字符串类型的转换
Converting a slice of bytes to a string type yields a string whose successive bytes are the elements of the slice.
将字节切片转换为字符串类型会产生一个字符串,其连续字节是切片的元素。
1 2 3 4 5 6 7 8 9 10
string([]byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø" string([]byte{}) // "" string([]byte(nil)) // "" type bytes []byte string(bytes{'h', 'e', 'l', 'l', '\xc3', '\xb8'}) // "hellø" type myByte byte string([]myByte{'w', 'o', 'r', 'l', 'd', '!'}) // "world!" myString([]myByte{'\xf0', '\x9f', '\x8c', '\x8d'}) // "🌍"
Converting a slice of runes to a string type yields a string that is the concatenation of the individual rune values converted to strings.
将符文切片转换为字符串类型会产生一个字符串,该字符串是转换为字符串的各个符文值的连接。
1 2 3 4 5 6 7 8 9 10
string([]rune{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔" string([]rune{}) // "" string([]rune(nil)) // "" type runes []rune string(runes{0x767d, 0x9d6c, 0x7fd4}) // "\u767d\u9d6c\u7fd4" == "白鵬翔" type myRune rune string([]myRune{0x266b, 0x266c}) // "\u266b\u266c" == "♫♬" myString([]myRune{0x1f30e}) // "\U0001f30e" == "🌎"
Converting a value of a string type to a slice of bytes type yields a non-nil slice whose successive elements are the bytes of the string.
将字符串类型的值转换为字节切片类型会产生一个非零切片,其连续元素是字符串的字节。
1 2 3 4 5 6 7
[]byte("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'} []byte("") // []byte{} bytes("hellø") // []byte{'h', 'e', 'l', 'l', '\xc3', '\xb8'} []myByte("world!") // []myByte{'w', 'o', 'r', 'l', 'd', '!'} []myByte(myString("🌏")) // []myByte{'\xf0', '\x9f', '\x8c', '\x8f'}
Converting a value of a string type to a slice of runes type yields a slice containing the individual Unicode code points of the string.
将字符串类型的值转换为符文类型切片会产生一个切片,其中包含字符串的各个 Unicode 代码点。
1 2 3 4 5 6 7
[]rune(myString("白鵬翔")) // []rune{0x767d, 0x9d6c, 0x7fd4} []rune("") // []rune{} runes("白鵬翔") // []rune{0x767d, 0x9d6c, 0x7fd4} []myRune("♫♬") // []myRune{0x266b, 0x266c} []myRune(myString("🌐")) // []myRune{0x1f310}
Finally, for historical reasons, an integer value may be converted to a string type. This form of conversion yields a string containing the (possibly multi-byte) UTF-8 representation of the Unicode code point with the given integer value. Values outside the range of valid Unicode code points are converted to
"\uFFFD"
.最后,出于历史原因,整数值可以转换为字符串类型。这种形式的转换会产生一个字符串,其中包含具有给定整数值的 Unicode 代码点的(可能为多字节)UTF-8 表示形式。超出有效 Unicode 代码点范围的值将转换为
"\uFFFD"
。
|
|
Note: This form of conversion may eventually be removed from the language. The go vet
tool flags certain integer-to-string conversions as potential errors. Library functions such as utf8.AppendRune
or utf8.EncodeRune
should be used instead.
注意:这种形式的转换最终可能会从语言中删除。 go vet
工具将某些整数到字符串的转换标记为潜在错误。应改用库函数,例如 utf8.AppendRune
或 utf8.EncodeRune
。
Conversions from slice to array pointer 从切片到数组指针的转换
Converting a slice to an array yields an array containing the elements of the underlying array of the slice. Similarly, converting a slice to an array pointer yields a pointer to the underlying array of the slice. In both cases, if the length of the slice is less than the length of the array, a run-time panic occurs.
将切片转换为数组会产生一个数组,其中包含切片的底层数组的元素。类似地,将切片转换为数组指针会产生一个指向切片的底层数组的指针。在这两种情况下,如果切片长度小于数组长度,则会发生运行时恐慌。
|
|
个人注释
给出以上示例的完整示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
package main import "fmt" func main() { s := make([]byte, 2, 4) s0 := (*[0]byte)(s) // s0 != nil fmt.Printf("%T,%#v\n", s0, s0) // *[0]uint8,&[0]uint8{} s1 := (*[1]byte)(s[1:]) // &s1[0] == &s[1] fmt.Printf("%T,%#v\n", s1, s1) // *[1]uint8,&[1]uint8{0x0} s2 := (*[2]byte)(s) // &s2[0] == &s[0] fmt.Printf("%T,%#v\n", s2, s2) // *[2]uint8,&[2]uint8{0x0, 0x0} // panic: runtime error: cannot convert slice with length 2 to array or pointer to array with length 4 //s4 := (*[4]byte)(s) // panics: len([4]byte) > len(s) //fmt.Printf("%T,%#v\n", s4, s4) // var t []string t0 := (*[0]string)(t) // t0 == nil fmt.Printf("%T,%#v\n", t0, t0) // *[0]string,(*[0]string)(nil) // panic: runtime error: cannot convert slice with length 0 to array or pointer to array with length 1 //t1 := (*[1]string)(t) // panics: len([1]string) > len(t) //fmt.Printf("%T,%#v\n", t1, t1) // u := make([]byte, 0) u0 := (*[0]byte)(u) // u0 != nil fmt.Printf("%T,%#v\n", u0, u0) //*[0]uint8,&[0]uint8{} }
个人注释
以上可以将切片转为数组指针,那么是否可以将切片转换为数组?=》 可以
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
package main import "fmt" func main() { s := make([]byte, 2, 4) s0 := ([0]byte)(s) // s0 != nil fmt.Printf("%T,%#v\n", s0, s0) // [0]uint8,[0]uint8{} s1 := ([1]byte)(s[1:]) // &s1[0] == &s[1] fmt.Printf("%T,%#v\n", s1, s1) // [1]uint8,[1]uint8{0x0} s2 := ([2]byte)(s) // &s2[0] == &s[0] fmt.Printf("%T,%#v\n", s2, s2) // [2]uint8,[2]uint8{0x0, 0x0} // panic: runtime error: cannot convert slice with length 2 to array or pointer to array with length 4 //s4 := ([4]byte)(s) // panics: len([4]byte) > len(s) //fmt.Printf("%T,%#v\n", s4, s4) // var t []string t0 := ([0]string)(t) // t0 == nil fmt.Printf("%T,%#v\n", t0, t0) // [0]string,[0]string{} // panic: runtime error: cannot convert slice with length 0 to array or pointer to array with length 1 t1 := ([1]string)(t) // panics: len([1]string) > len(t) fmt.Printf("%T,%#v\n", t1, t1) // u := make([]byte, 0) u0 := ([0]byte)(u) // u0 != nil fmt.Printf("%T,%#v\n", u0, u0) //[0]uint8,[0]uint8{} }
Constant expressions 常量表达式
Constant expressions may contain only constant operands and are evaluated at compile time.
常量表达式可以只包含常量操作数,并在编译时进行求值。
Untyped boolean, numeric, and string constants may be used as operands wherever it is legal to use an operand of boolean, numeric, or string type, respectively.
无类型的布尔、数值和字符串常量可以在需要布尔、数值或字符串类型操作数的地方使用。
A constant comparison always yields an untyped boolean constant. If the left operand of a constant shift expression is an untyped constant, the result is an integer constant; otherwise it is a constant of the same type as the left operand, which must be of integer type.
常量比较总是产生一个无类型的布尔常量。如果常量移位表达式的左操作数是一个无类型的常量,那么结果就是一个整型常量;否则就是一个与左操作数相同类型的常量(左操作数必须是整型)。
Any other operation on untyped constants results in an untyped constant of the same kind; that is, a boolean, integer, floating-point, complex, or string constant. If the untyped operands of a binary operation (other than a shift) are of different kinds, the result is of the operand’s kind that appears later in this list: integer, rune, floating-point, complex. For example, an untyped integer constant divided by an untyped complex constant yields an untyped complex constant.
对无类型常量的任何其他操作都会得到一个相同类型的无类型常量,也就是布尔、整数、浮点、复数或字符串常量。如果一个二元运算(除移位外)的无类型操作数是不同种类的,那么结果就是出现在如下列表的操作数类型:整数,符文,浮点,复数。例如,一个无类型的整数常量除以一个无类型的复数常量,得到一个无类型的复数常量。
|
|
Applying the built-in function complex
to untyped integer, rune, or floating-point constants yields an untyped complex constant.
将内置函数 complex
应用于无类型的整数、符文或浮点常量,可以得到一个无类型的复数常量。
|
|
Constant expressions are always evaluated exactly; intermediate values and the constants themselves may require precision significantly larger than supported by any predeclared type in the language. The following are legal declarations:
常量表达式总是被精确地求值
;中间值和常量本身可能需要比语言中任何预先声明的类型所支持的精度大得多
。以下是合法的声明:
|
|
The divisor of a constant division or remainder operation must not be zero:
常量除法或取余操作的除数一定不能为零
。
|
|
The values of typed constants must always be accurately representable by values of the constant type. The following constant expressions are illegal:
类型常量的值必须总是可以准确地由常量类型的值来表示。下面的常量表达式是非法的:
|
|
The mask used by the unary bitwise complement operator ^
matches the rule for non-constants: the mask is all 1s for unsigned constants and -1 for signed and untyped constants.
一元按位补运算符^
使用的掩码符合非常量的规则:对于无符号常量来说是所有(掩码)位都是1
,对于有符号和无类型的常量来说是-1
。=> 仍有疑问??
|
|
Implementation restriction: A compiler may use rounding while computing untyped floating-point or complex constant expressions; see the implementation restriction in the section on constants. This rounding may cause a floating-point constant expression to be invalid in an integer context, even if it would be integral when calculated using infinite precision, and vice versa.
实现限制:编译器在计算无类型浮点或复数常量表达式时可能会使用舍入,请参见常量部分的实现限制。这种舍入可能会导致浮点常量表达式在整数上下文中无效,即使它在使用无限精度计算时是整数,反之亦然。
Order of evaluation 求值顺序
At package level, initialization dependencies determine the evaluation order of individual initialization expressions in variable declarations. Otherwise, when evaluating the operands of an expression, assignment, or return statement, all function calls, method calls, [receive operations](https://go.dev/ref/spec#Receive operator), and binary logical operations are evaluated in lexical left-to-right order.
在包级别上,初始化依赖项决定了变量声明中各个初始化表达式的求值顺序。除此之外,在求值表达式、赋值或返回语句的操作数时,所有的函数调用、方法调用和通信操作都是按词法从左到右的顺序求值的。
For example, in the (function-local) assignment
例如,在(函数内部)赋值语句中
|
|
the function calls and communication happen in the order f()
, h()
(if z
evaluates to false), i()
, j()
, <-c
, g()
, and k()
. However, the order of those events compared to the evaluation and indexing of x
and the evaluation of y
and z
is not specified, except as required lexically. For instance, g
cannot be called before its arguments are evaluated.
函数调用和通信发生的顺序是f()
, h()
, i(),
j()
, <-c
, g()
, 和k()
。然而,与x
的求值和索引以及y
的求值相比,这些事件的顺序没有被指定。
|
|
At package level, initialization dependencies override the left-to-right rule for individual initialization expressions, but not for operands within each expression:
在包级别上,对于独立的初始化表达式来说,初始化依赖项会覆盖其从左到右的求值规则,但不覆盖每个表达式中的操作数:
|
|
The function calls happen in the order u()
, sqr()
, v()
, f()
, v()
, and g()
.
函数调用按照u()
、sqr()
、v()
、f()
、v()
、g()
的顺序发生。
Floating-point operations within a single expression are evaluated according to the associativity of the operators. Explicit parentheses affect the evaluation by overriding the default associativity. In the expression x + (y + z)
the addition y + z
is performed before adding x
.
单个表达式中的浮点运算是按照运算符的结合性来求值的。显式的括号会通过覆盖默认的结合性来影响求值。在表达式x + (y + z)
中,加法y + z
会在加x
之前进行。