Go 編程:那些隱晦的操做符

原文發佈於我的站點: GitDiG.com, 原文連接: Go 編程:那些隱晦的操做符

本篇做爲 Go 編程「邊角料」的最後一篇,主要針對 Go 語言提供的操做符進行一次總結。恰好迴應上篇一位讀者關於表達式是否要加'.'的問題作個回覆。html

在 Go 語言中,一共提供了47個操做符,包括標點符號。摘自官方文檔,分別是:git

+    &     +=    &=     &&    ==    !=    (    )
-    |     -=    |=     ||    <     <=    [    ]
*    ^     *=    ^=     <-    >     >=    {    }
/    <<    /=    <<=    ++    =     :=    ,    ;
%    >>    %=    >>=    --    !     ...   .    :
     &^          &^=

除以上操做符之外,在 Go 語言中還有一個特殊的符號 _, 以及一個非 Go 語言操做符的特殊字節?github

刨去一些經常使用的操做符,對其中較隱晦操做符作個簡單的備註,方便不時之需。
就隱晦自己而言能夠劃分爲兩類:golang

  • 符號自己隱晦
  • 應用場景隱晦

1. 符號隱晦

上文中的 47 個操做符,一個個看下來,真正隱晦的符號基本上都是位運算操做符或相關操做符。
之因此隱晦,由於位運算在大部分開發人員的平常開發中屬於很是規操做,由於運用得少,而增長了其陌生感。不妨簡單羅列一下:sql

&    bitwise AND            integers     
|    bitwise OR             integers    
^    bitwise XOR            integers    
&^   bit clear (AND NOT)    integers  

<<   left shift             integer << unsigned integer
>>   right shift            integer >> unsigned integer

寫個簡單的例子, 強化記憶:編程

package main

import "fmt"

func main(){
  fmt.Printf("AND: a(%b) & b(%b) = (%b)\n", 4, 5, (4 & 5))
  fmt.Printf("OR:  a(%b) | b(%b) = (%b)\n", 4, 5, (4 | 5))
  fmt.Printf("XOR: a(%b) ^ b(%b) = (%b)\n", 4, 5, (4 ^ 5))
  fmt.Printf("AND NOT: a(%b) &^ b(%b) = (%b)\n", 4, 5, (4 &^ 5))

  fmt.Printf("Left Shift:  a(%b) << 1 = (%b)\n", 5, (5 << 1))
  fmt.Printf("Right Shift: a(%b) >> 1 = (%b)\n", 5, (5 >> 1))
}

輸出的結果是:c#

AND: a(100) & b(101) = (100)
OR:  a(100) | b(101) = (101)
XOR: a(100) ^ b(101) = (1)
AND NOT: a(100) &^ b(101) = (0)
Left Shift:  a(101) << 1 = (1010)
Right Shift: a(101) >> 1 = (10)

位操做符並不難,之因此隱晦,主要是實際運用的少致使的。其中,XOR 運算有個特色:若是對一個值連續作兩次 XOR,會返回這個值自己。XOR 的這個特色,使得它能夠用於信息的加密。阮一峯這篇文章XOR 加密簡介很好讀。數組

與位運算符相關的符號,有:ide

<<=    >>=    &=    ^=   |=

其功能與+=是同樣的,即 a += 1 等同於 a = a + 1函數

2. 場景隱晦

另外一類操做符,看似很是簡單,但因其在不一樣應用場景下產生了不一樣功能效果,致使在使用上的陌生。

2.1 符號 '_'

符號 '_', 又稱爲空標識符(Blank identifier)。它有兩種使用場景,不一樣場景提供的功能是不一樣的.

  • 做爲匿名變量賦值使用

此時符號 '_', 功能與 /dev/null 相似,只負責接收值並直接丟棄,沒法取回。

ar := [10]int{1,2,3,4,5,6,7,8,9,0}
for _, v := range ar {
    println(v)
}
  • 在包引用時使用

常規狀況下,包引用格式是這樣的:

package YourPackage

import   "lib/math"         //math.Sin
import m "lib/math"         //m.Sin
import . "lib/math"         //Sin

具體語法意義不解釋了。如今看看 '_' 在包引入中的功能。

import _ "the/third/pkg"

此時引入的第三方包"the/third/pkg",若是引入的結果是一個空標識符'_'。按其空標識符的原始意義,就是對於使用方而言,沒有任何意義,由於沒法使用被引入包中任何變量或是函數。

可是,這種引用有一個反作用,就是:會對第三方包進行編譯而且執行初始化func init()操做.這一功能,對於某些引用方就很是有用。

因此當咱們研究一些開源代碼時,看到相似的引用import _ "the/third/pkg"時,直接跳到引入包的init函數,就能夠創建起內在邏輯。不妨看一下github.com/golang/protobuf/protoc-gen-go/link_grpc.go的代碼, 這就是grpc插件註冊到protoc-gen-go的地方。

package main

import _ "github.com/golang/protobuf/protoc-gen-go/grpc"

2.2 符號 '.'

符號 '.' 常規狀況下是做爲選擇器的在使用。如:

//直接選擇屬性名或函數名
x.FieldName
x.FunctionName

還能夠作爲包引用使用,如上節。

import . "lib/math"         //Sin

它的做用有點相似當前目錄符'.'的意思了,簡化掉了包引用的相對路徑。

還有一個用法,即類型斷言(type assertion)。

//類型斷言: 類型必須用'()'括起來
v, ok := x.(T)

做爲類型斷言時,類型必須用'()'括起來,防止和選擇器功能混淆。類型斷言類型轉換須要區分一下。

//類型轉換: 變量必須用'()'括起來
v := T(x)

區別:

  • 類型轉換中,待轉換的變量x只要是一個能夠轉換成目標類型的變量便可。失敗時代碼沒法編譯經過。
  • 類型斷言中,待斷言的變量x必須與目標類型一致。若是失敗,返回bool參數標識。

2.3 符號 '...'

符號 '...' 主要用於不定參數與切片打散功能。很是簡單,備註一下。

不定參數

import "fmt"

func Foo(args ...interface{}) {
  for _, arg := range args {
    fmt.Println(arg)
  }
}

切片打散

args := []interface{}{1, false, "hello"}
Foo(args...)

數組長度

[...]int{1,2,4}

2.4 符號 '?', 非 Go 語言操做符

不少語言都支持符號 '?', 可是在 Go 語言中並它不屬於系統操做符, 雖然在 Go 代碼中常常會碰到符號 '?'。在語言級別符號 '?' 沒有任何語法意義,只是一個常規的字節。

常見使用場景是作爲 SQL 語句的替換符使用。如:

import "database/sql"

    id := 47
    result, err := db.ExecContext(ctx, "UPDATE balances SET balance = balance + 10 WHERE user_id = ?", id)
    if err != nil {
        log.Fatal(err)
    }

其中的符號 '?' 僅僅與依賴包database/sql有關,與 Go 語言自己無關。在database/sql包中,字符 '?' 能夠將任意類型參數變量替換轉義成 SQL 字符串合適的類型值。

3. 小結

以上收集的操做符僅僅是個簡單的小結,可能更多的應用場景沒有關注到,歡迎指正。

相關文章
相關標籤/搜索