This article will list all kinds of syntax/semantics exceptions in Go. Some of these exceptions are syntactic sugars to make programming convenient, some are caused built-in generic privileges, some exists for history reasons, and some exists for other reasons in logic.
package main
import (
"fmt"
)
func f0() float64 {return 1}
func f1() (float64, float64) {return 1, 2}
func f2(float64, float64) {}
func f3(float64, float64, float64) {}
func f4()(x, y []int) {return}
func f5()(x map[int]int, y int) {return}
type I interface {m()(float64, float64)}
type T struct{}
func (T) m()(float64, float64) {return 1, 2}
func main() {
// These lines compile okay.
f2(f0(), 123)
f2(f1())
fmt.Println(f1())
_ = complex(f1())
_ = complex(T{}.m())
f2(I(T{}).m())
// These lines don't compile.
/*
f3(123, f1())
f3(f1(), 123)
*/
// This line compiles okay only since
// Go Toolchain 1.15.
println(f1())
// The following 3 lines compile okay
// only since Go Toolchain 1.13.
copy(f4())
delete(f5())
_ = complex(I(T{}).m())
}
package main
type T struct {
x int
}
func main() {
var t T
var p = &t
p.x *= 2
// The above line is a sugar of the following line.
(*p).x *= 2
}
*T
are not methods of type T
for sure.
*T
are not methods of type T
,
addressable values of type T
can be used as the receiver arguments of calls to these methods.
package main
type T struct {
x int
}
func (pt *T) Double() {
pt.x *= 2
}
func main() {
// T{3}.Double() // This line fails to compile.
var t = T{3}
t.Double() // t.x == 6 now
// The above line is a sugar of the following line.
(&t).Double() // t.x == 12 now
}
Please read structs and containers for details.
x
is a value of a named one-level pointer type,
and selector (*x).f
is a legal selector,
then the x.f
is also a legal selector, it can be viewed as a shorthand of (*x).f
.
Selectors can never be used on values of multi-level pointer types, no matter whether the multi-level pointer types are named or not.
Exception of the sugar:f
denotes a struct field,
it is not valid if f
denotes a method.
package main
type T struct {
x int
}
func (T) y() {
}
type P *T
type PP **T // a multi-level pointer type
func main() {
var t T
var p P = &t
var pt = &t // type of pt is *T
var ppt = &pt // type of ppt is **T
var pp PP = ppt
_ = pp
_ = (*p).x // legal
_ = p.x // also legal (for x is a field)
_ = (*p).y // legal
// _ = p.y // illegal (for y is a method)
// Following ones are all illegal.
/*
_ = ppt.x
_ = ppt.y
_ = pp.x
_ = pp.y
*/
}
package main
func main() {
var m = map[string]int{"abc": 123}
_ = &m // okay
// The exception:
// p = &m["abc"] // error: map elements are unaddressable
// The sugar:
f := func() []int { // return results are unaddressable
return []int{0, 1, 2}
}
// _ = &f() // error: f() is unaddressable
_ = &f()[2] // okay
}
package main
func main() {
type T struct {
x int
}
var mt = map[string]T{"abc": {123}}
// Map elements are unaddressable.
// _ = &mt["abc"] // error
// Partial modification is not allowed.
// mt["abc"].x = 456 // error
// It is ok to replace a map element as a whole.
mt["abc"] = T{x: 789}
}
make
and new
functions are types.
init
(and types as func()
).
init
functions can't be called in user code.
init
functions can not be used as function values.
package main
import "fmt"
import "unsafe"
func init() {}
func main() {
// These ones are okay.
var _ = main
var _ = fmt.Println
// This one fails to compile.
var _ = init
}
new
generic function is enclosed in parentheses.
The type argument of an instantiation of the built-in make
generic function is mixed with value arguments
and these type and value arguments are enclosed in parentheses.
builtin
and unsafe
standard packages, can't be discarded, if the called function has return results.
copy
and recover
functions
can be all discarded, even if the two functions have return results.
nil
variable is not addressable.
nil
is an immutable variable.
copy
and append
function call
is a byte slice, then the second argument can be a string,
whereas a string value is not assignable to the second parameter type (also a byte slice).
(For an append
call, assume the second argument is passed with the form arg...
.)
package main
func main() {
var bs = []byte{1, 2, 3}
var s = "xyz"
copy(bs, s)
// The above line is a sugar (and an optimization)
// for the following line.
copy(bs, []byte(s))
bs = append(bs, s...)
// The above line is a sugar (and an optimization)
// for the following line.
bs = append(bs, []byte(s)...)
}
nil
identifier.
package main
func main() {
var s1 = []int{1, 2, 3}
var s2 = []int{7, 8, 9}
//_ = s1 == s2 // error: slice values can't be compared
_ = s1 == nil // ok
_ = s2 == nil // ok
var m1 = map[string]int{}
var m2 = m1
// _ = m1 == m2 // error: map values can't be compared
_ = m1 == nil
_ = m2 == nil
var f1 = func(){}
var f2 = f1
// _ = f1 == f2 // error: functions can't be compared
_ = f1 == nil
_ = f2 == nil
}
T
can be represented with composite literals, then T{}
is its zero value.
T
, T{}
isn't its zero value.
Its zero value is represented with nil
.
package main
import "fmt"
func main() {
// new(T) returns the address of a zero value of type T.
type T0 struct {
x int
}
fmt.Println( T0{} == *new(T0) ) // true
type T1 [5]int
fmt.Println( T1{} == *new(T1) ) // true
type T2 []int
fmt.Println( T2{} == nil ) // false
type T3 map[int]int
fmt.Println( T3{} == nil ) // false
}
error
type has a Error() string
method.
nil
has neither a type nor a default type.
iota
is a built-in constant which is bound with 0
,
but its value is not constant. Its value will start from 0
and increase one constant specification by constant specification in a constant declaration,
though the increments happen at compile time.
iota
can only be used within constant declarations.
It can't be assigned to variables in variable declarations.
package main
func main() {
var ok bool
var m = map[int]int{}
_, ok = m[123] // will not panic
_ = m[123] // will not panic
var c = make(chan int, 2)
c <- 123
close(c)
_, ok = <-c // will not panic
_ = <-c // will not panic
var v interface{} = "abc"
_, ok = v.(int) // will not panic
_ = v.(int) // will panic!
}
else
keyword followed by a code blockelse
keyword must be followed by an explicit code block {...}
.
else
keyword may be followed by a if
code block (which is implicit).
foo
compiles okay, but function bar
doesn't.
func f() []int {
return nil
}
func foo() {
if vs := f(); len(vs) == 0 {
} else if len(vs) == 1 {
}
if vs := f(); len(vs) == 0 {
} else {
switch {
case len(vs) == 1:
default:
}
}
if vs := f(); len(vs) == 0 {
} else {
for _, _ = range vs {
}
}
}
func bar() {
if vs := f(); len(vs) == 0 {
} else switch { // error
case len(vs) == 1:
default:
}
if vs := f(); len(vs) == 0 {
} else for _, _ = range vs { // error
}
}
The Go 101 프로젝트는 Github 에서 호스팅됩니다. 오타, 문법 오류, 부정확한 표현, 설명 결함, 코드 버그, 끊어진 링크와 같은 모든 종류의 실수에 대한 수정 사항을 제출하여 Go 101을 개선을 돕는 것은 언제나 환영합니다.
주기적으로 Go에 대한 깊이 있는 정보를 얻고 싶다면 Go 101의 공식 트위터 계정인 @go100and1을 팔로우하거나 Go 101 슬랙 채널에j가입해주세요.
reflect
표준 패키지sync
표준 패키지sync/atomic
표준 패키지