99re热这里只有精品视频,7777色鬼xxxx欧美色妇,国产成人精品一区二三区在线观看,内射爽无广熟女亚洲,精品人妻av一区二区三区

Go語言 Go中的一些語法/語義例外

2023-02-16 17:40 更新

本篇文章將列出Go中的各種語法和語義例外。 這些例外中的一些屬于方便編程的語法糖,一些屬于內(nèi)置泛型特權(quán),一些源于歷史原因或者其它各種邏輯原因。

嵌套函數(shù)調(diào)用

基本規(guī)則:

如果一個函數(shù)(包括方法)調(diào)用的返回值個數(shù)不為零,并且它的返回值列表可以用做另一個函數(shù)調(diào)用的實參列表,則此前者調(diào)用可以被內(nèi)嵌在后者調(diào)用之中,但是此前者調(diào)用不可和其它的實參混合出現(xiàn)在后者調(diào)用的實參列表中。

語法糖:

如果一個函數(shù)調(diào)用剛好返回一個結(jié)果,則此函數(shù)調(diào)用總是可以被當作一個單值實參用在其它函數(shù)調(diào)用中,并且此函數(shù)調(diào)用可以和其它實參混合出現(xiàn)在其它函數(shù)調(diào)用的實參列表中。

例子:

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() {
	// 這些行編譯沒問題。
	f2(f0(), 123)
	f2(f1())
	fmt.Println(f1())
	_ = complex(f1())
	_ = complex(T{}.m())
	f2(I(T{}).m())

	// 這些行編譯不通過。
	/*
	f3(123, f1())
	f3(f1(), 123)
	*/

	// 此行從Go官方工具鏈1.15開始才能夠編譯通過。
	println(f1())

	// 下面這三行從Go官方工具鏈1.13開始才能夠編譯通過。
	copy(f4())
	delete(f5())
	_ = complex(I(T{}).m())
}

選擇結(jié)構(gòu)體字段值

基本規(guī)則:

指針類型和值沒有字段。

語法糖:

我們可以通過一個結(jié)構(gòu)體值的指針來選擇此結(jié)構(gòu)體的字段。

例子:

package main

type T struct {
	x int
}

func main() {
	var t T
	var p = &t

	p.x *= 2
	// 上一行是下一行的語法糖。
	(*p).x *= 2
}

方法調(diào)用的屬主實參

基本規(guī)則:

為類型?*T?顯式聲明的方法肯定不是類型?T?的方法。

語法糖:

盡管為類型?*T?顯式聲明的方法肯定不是類型?T?的方法,但是可尋址的?T?值可以用做這些方法的調(diào)用的屬主實參。

例子:

package main

type T struct {
	x int
}

func (pt *T) Double() {
	pt.x *= 2
}

func main() {
	// T{3}.Double() // error: T值沒有Double方法。

	var t = T{3}

	t.Double() // 現(xiàn)在:t.x == 6
	// 上一行是下一行的語法糖。
	(&t).Double() // 現(xiàn)在:t.x == 12
}

取組合字面量的地址

基本規(guī)則:

組合字面量是不可尋址的,并且是不可尋址的值是不能被取地址的。

語法糖:

盡管組合字面量是不可尋址的,它們?nèi)钥梢员伙@式地取地址。

請閱讀結(jié)構(gòu)體內(nèi)置容器類型兩篇文章獲取詳情。

指針值和選擇器

基本規(guī)則:

一般說來,具名的指針類型的值不能使用在選擇器語法形式中。

語法糖:

如果值?x?的類型為一個一級具名指針類型,并且?(*x).f?是一個合法的選擇器,則?x.f?也是合法的。

任何多級指針均不能出現(xiàn)在選擇器語法形式中。

上述語法糖的例外:

上述語法糖只對?f?為字段的情況才有效,對于?f?為方法的情況無效。

例子:

package main

type T struct {
	x int
}

func (T) y() {
}

type P *T
type PP **T // 一個多級指針類型

func main() {
	var t T
	var p P = &t
	var pt = &t   // pt的類型為*T
	var ppt = &pt // ppt的類型為**T
	var pp PP = ppt
	_ = pp

	_ = (*p).x // 合法
	_ = p.x    // 合法(因為x為一個字段)

	_ = (*p).y // 合法
	// _ = p.y // 不合法(因為y為一個方法)

	// 下面的選擇器均不合法。
	/*
	_ = ppt.x
	_ = ppt.y
	_ = pp.x
	_ = pp.y
	*/
}

容器和容器元素的可尋址性

基本規(guī)則:

如果一個容器值是可尋址的,則它的元素也是可尋址的。

例外:

一個映射值的元素總是不可尋址的,即使此映射本身是可尋址的。

語法糖:

一個切片值的元素總是可尋址的,即使此切片值本身是不可尋址的。

例子:

package main

func main() {
	var m = map[string]int{"abc": 123}
	_ = &m // okay

	// 例外。
	// p = &m["abc"] // error: 映射元素不可尋址

	// 語法糖。
	f := func() []int {
		return []int{0, 1, 2}
	}
	// _ = &f() // error: 函數(shù)調(diào)用是不可尋址的
	_ = &f()[2] // okay
}

修改值

基本規(guī)則:

不可尋址的值不可修改。

例外:

盡管映射元素是不可尋址的,但是它們可以被修改(但是它們必須被整個覆蓋修改)。

例子:

package main

func main() {
	type T struct {
		x int
	}

	var mt = map[string]T{"abc": {123}}
	// _ = &mt["abc"]     // 映射元素是不可尋址的
	// mt["abc"].x = 456  // 部分修改是不允許的
	mt["abc"] = T{x: 789} // 整體覆蓋修改是可以的
}

函數(shù)參數(shù)

基本規(guī)則:

函數(shù)的每個參數(shù)一般為某個類型一個值。

例外:

內(nèi)置?make?和?new?函數(shù)的第一個參數(shù)為一個類型。

同一個代碼包中的函數(shù)命名

基本規(guī)則:

同一個代碼包中聲明的函數(shù)的名稱不能重復(fù)。

例外:

同一個代碼包中可以聲明若干個名為?init?類型為?func()?的函數(shù)。

函數(shù)調(diào)用

基本規(guī)則:

名稱為非空標識符的函數(shù)可以被調(diào)用。

例外:

?init?函數(shù)不可被調(diào)用。

函數(shù)值

基本規(guī)則:

聲明的函數(shù)可以被用做函數(shù)值。

例外:

?init?函數(shù)不可被用做函數(shù)值。

例子:

package main

import "fmt"
import "unsafe"

func init() {}

func main() {
	// 這兩行編譯沒問題。
	var _ = main
	var _ = fmt.Println

	// 下面這行編譯不通過。
	var _ = init
}

泛型類型實參的傳遞方式

基本規(guī)則:

在泛型類型實參列表中,所有實參均包裹在同一對方括號中,各個實參之間使用逗號分開

例外:

內(nèi)置泛型類型的類型實參傳遞形態(tài)各異。映射類型的鍵值類型實參單獨包裹在一對方括號中,其它實參并沒有被包裹。 內(nèi)置?new?泛型函數(shù)的類型實參是包裹在一對圓括號中。 內(nèi)置?make?泛型函數(shù)的類型實參是和值實參混雜在一起并包裹在同一對圓括號中。

舍棄函數(shù)調(diào)用返回值

基本規(guī)則:

一個函數(shù)調(diào)用的所有返回值可以被一并忽略舍棄。

例外:

內(nèi)置函數(shù)(展示在?builtin?和?unsafe?標準庫包中的函數(shù))調(diào)用的返回值不能被舍棄。

例外中的例外:

內(nèi)置函數(shù)?copy?和?recover?的調(diào)用的返回值可以被舍棄。

聲明的變量

基本規(guī)則:

聲明的變量總是可尋址的。

例外:

預(yù)聲明的nil變量是不可尋址的。

所以,預(yù)聲明的nil是一個不可更改的變量。

傳參

基本規(guī)則:

當一個實參被傳遞給對應(yīng)的形參時,此實參必須能夠賦值給此形參類型。

語法糖:

如果內(nèi)置函數(shù)?copy?和?append?的一個調(diào)用的第一個形參為一個字節(jié)切片(這時,一般來說,第二形參也應(yīng)該是一個字節(jié)切片),則第二個形參可以是一個字符串,即使字符串不能被賦給一個字節(jié)切片。 (假設(shè)?append?函數(shù)調(diào)用的第二個形參使用?arg...?形式傳遞。)

例子:

package main

func main() {
	var bs = []byte{1, 2, 3}
	var s = "xyz"

	copy(bs, s)
	// 上一行是下一行的語法糖和優(yōu)化。
	copy(bs, []byte(s))

	bs = append(bs, s...)
	// 上一行是下一行的語法糖和優(yōu)化。
	bs = append(bs, []byte(s)...)
}

比較

基本規(guī)則:

映射、切片和函數(shù)類型是不支持比較的。

例外:

映射、切片和函數(shù)值可以和預(yù)聲明標識符?nil?比較。

例子:

package main

func main() {
	var s1 = []int{1, 2, 3}
	var s2 = []int{7, 8, 9}
	//_ = s1 == s2 // error: 切片值不可比較。
	_ = s1 == nil  // ok
	_ = s2 == nil  // ok

	var m1 = map[string]int{}
	var m2 = m1
	// _ = m1 == m2 // error: 映射值不可比較。
	_ = m1 == nil   // ok
	_ = m2 == nil   // ok

	var f1 = func(){}
	var f2 = f1
	// _ = f1 == f2 // error: 函數(shù)值不可比較。
	_ = f1 == nil   // ok
	_ = f2 == nil   // ok
}

比較二

基本規(guī)則:

如果一個值可以隱式轉(zhuǎn)換為一個可比較類型,則這此值和此可比較類型的值可以用?==?和?!=?比較符來做比較。

例外:

一個不可比較類型(一定是一個非接口類型)的值不能和一個接口類型的值比較,即使此不可比較類型實現(xiàn)了此接口類型(從而此不可比較類型的值可以被隱式轉(zhuǎn)換為此接口類型)。

請閱讀值比較規(guī)則獲取詳情。

空組合字面量

基本規(guī)則:

如果一個類型?T?的值可以用組合字面量表示,則?T{}?表示此類型的零值。

例外:

對于一個映射或者切片類型?T?,?T{}?不是它的零值,它的零值使用預(yù)聲明的?nil?表示。

例子:

package main

import "fmt"

func main() {
	// new(T)返回類型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
}

容器元素遍歷

基本規(guī)則:

只有容器值可以跟在?range?關(guān)鍵字后,?for-range?循環(huán)遍歷出來的是容器值的各個元素。 每個容器元素對應(yīng)的鍵值(或者索引)也將被一并遍歷出來。

例外1:

當?range?關(guān)鍵字后跟的是字符串時,遍歷出來的是碼點值,而不是字符串的各個元素byte字節(jié)值。

例外2:

當?range?關(guān)鍵字后跟的是通道時,通道的元素的鍵值(次序)并未被一同遍歷出來。

語法糖:

盡管數(shù)組指針不屬于容器,但是?range?關(guān)鍵字后可以跟數(shù)組指針來遍歷數(shù)組元素。

內(nèi)置類型的方法

基本規(guī)則:

內(nèi)置類型都沒有方法。

例外:

內(nèi)置類型?error?有一個?Error() string?方法。

值的類型

基本規(guī)則:

每個值要么有一個確定的類型要么有一個默認類型。

例外:

類型不確定的?nil?值既沒有確定的類型也沒有默認類型。

常量值

基本規(guī)則:

常量值的值固定不變。常量值可以被賦給變量值。

例外1:

預(yù)聲明的?iota?是一個綁定了?0?的常量,但是它的值并不固定。 在一個包含多個常量描述的常量聲明中,如果一個?iota?的值出現(xiàn)在一個常量描述中,則它的值將被自動調(diào)整為此常量描述在此常量聲明中的次序值,盡管此調(diào)整發(fā)生在編譯時刻。

例外2:

?iota?只能被用于常量聲明中,它不能被賦給變量。

舍棄表達式中可選的結(jié)果值對程序行為的影響

基本規(guī)則:

表達式中可選的結(jié)果值是否被舍棄不會對程序行為產(chǎn)生影響。

例外:

當一個失敗的類型斷言表達式的可選的第二個結(jié)果值被舍棄時,當前協(xié)程將產(chǎn)生一個恐慌。

例子:

package main

func main() {
	var ok bool

	var m = map[int]int{}
	_, ok = m[123]
	_ = m[123] // 不會產(chǎn)生恐慌

	var c = make(chan int, 2)
	c <- 123
	close(c)
	_, ok = <-c
	_ = <-c // 不會產(chǎn)生恐慌

	var v interface{} = "abc"
	_, ok = v.(int)
	_ = v.(int) // 將產(chǎn)生一個恐慌!
}

else關(guān)鍵字后跟隨另一個代碼塊

基本規(guī)則:

?else?關(guān)鍵字后必須跟隨一個顯式代碼塊?{...}?。

語法糖:

?else?關(guān)鍵字后可以跟隨一個(隱式)?if?代碼塊,

比如,在下面這個例子中,函數(shù)foo編譯沒問題,但是函數(shù)bar編譯不過。

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
	}
}


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號