
目錄
unique包裝查找Golang文檔並不重要。有很多好的資源,只需選擇一個並開始學習旅程即可。我主要跟隨學習 - Miek Gieben。
注意:本文檔中的每個示例都存儲在按節命名的目錄中。我假設X節中的每個命令都將在示例/X目錄中執行,因此我不編寫完整的路徑到達腳本文件。
/* hello_world.go */
package main
import "fmt" // Implements formatted I/O
/* Say Hello-World */
func main () {
fmt . Printf ( "Hello World" )
}go build helloworld.go # Return an executable called helloworld./helloworldgo run helloworld.go /* When you declare a variable it is assigned the "natural" null value for the type */
var a int // a has a value of 0
var s string // s is assigned the zero string, which is ""
a = 26
s = "hello"
/* Declaring & assigning in Go is a two step process, but they may be combined */
a := 26 // In this case the variable type is deduced from the value. A value of 26 indicates an int for example.
b := "hello" // The type should be string
/* Multiple var declarations may also be grouped (import & const also allow this) */
var (
a int
b string
)
/* Multiple variables of the same type ca also be declared on a single line */
var a , b int
a , b := 26 , 9
/* A special name for a variable is _, any value assigned to it is discarded. */
_ , b := 26 , 9布爾: bool
數值:
int它具有適合您的機器的長度(32位機器-32位,64位機器-64位)int8 , int16 , int32 , int64和byte ( uint8的別名), uint8 , uint16 ,UINT16, uint32 , uint64 。float32 , float64 , /* numerical_types.go */
package main
func main () {
var a int
var b int32
b = a + a // Give an error: cannot use a + a (type int) as type int32 in assignment.
b = b + 5
}常數:在編譯時創建常數,並且只能是數字,字符串或布爾值。您可以使用iota列舉價值。
const (
a = iota // First use of iota will yield 0. Whenever iota is used again on a new line its value is incremented with 1, so b has a vaue of 1.
b
)字符串:
string 。注意!在Python(我最喜歡的編程語言)中,我可以將它們都用於字符串分配。 s1 := "Hello"
c := [] rune ( s ) // Convert s1 to an array of runes
c [ 0 ] := 'M'
s2 := string ( c ) // Create a new string s2 with the alteration
fmt . Printf ( "%s n " , s2 )符文: Rune是int32的別名,(使用字符串中的字符迭代時使用)。
複合數: complex128 (64位真實零件)或complex32 。
錯誤:GO具有一個專門用於錯誤的內置類型,稱為error.var e 。
GO 1.18帶來對通用類型的支持。 GO 1.18提供的通用實現遵循類型參數提案,並允許開發人員添加可選類型參數以輸入和函數聲明。結帳Golang的仿製藥教程。
package main
import "fmt"
type Number interface {
int64 | float64
}
func main () {
// Initialize a map for the integer values
ints := map [ string ] int64 {
"first" : 34 ,
"second" : 12 ,
}
// Initialize a map for the float values
floats := map [ string ] float64 {
"first" : 35.98 ,
"second" : 26.99 ,
}
fmt . Printf ( "Non-Generic Sums: %v and %v n " ,
SumInts ( ints ),
SumFloats ( floats ))
fmt . Printf ( "Generic Sums: %v and %v n " ,
SumIntsOrFloats [ string , int64 ]( ints ),
SumIntsOrFloats [ string , float64 ]( floats ))
fmt . Printf ( "Generic Sums, type parameters inferred: %v and %v n " ,
SumIntsOrFloats ( ints ),
SumIntsOrFloats ( floats ))
fmt . Printf ( "Generic Sums with Constraint: %v and %v n " ,
SumNumbers ( ints ),
SumNumbers ( floats ))
}
// SumInts adds together the values of m.
func SumInts ( m map [ string ] int64 ) int64 {
var s int64
for _ , v := range m {
s += v
}
return s
}
// SumFloats adds together the values of m.
func SumFloats ( m map [ string ] float64 ) float64 {
var s float64
for _ , v := range m {
s += v
}
return s
}
// SumIntsOrFloats sums the values of map m. It supports both floats and integers
// as map values.
func SumIntsOrFloats [ K comparable , V int64 | float64 ]( m map [ K ] V ) V {
var s V
for _ , v := range m {
s += v
}
return s
}
// SumNumbers sums the values of map m. Its supports both integers
// and floats as map values.
func SumNumbers [ K comparable , V Number ]( m map [ K ] V ) V {
var s V
for _ , v := range m {
s += v
}
return s
} Precedence Operator(s)
Highest * / % << >> & &^
`+ -
== != < <= > >=
<-
&&
Lowest ||
& bitwise和, |位或^位Xor, &^位分別清晰。 break default func interface select
case defer go map struct
chan else goto package switch
const fallthrough if range type
continue for import return var
var , const , package , import在先前的部分中使用。func用於聲明功能和方法。return用於從功能返回。go用於並發。select用於從不同類型的通信中進行選擇。interface .struct用於抽像數據類型。type 。 if x > 0 {
return y
} esle {
return x
}
if err := MagicFunction (); err != nil {
return err
}
// do somethinggoto ,您跳到了一個標籤,該標籤必須在當前功能中定義。 /* goto_test */
/* Create a loop */
func gototestfunc () {
i := 0
Here:
fmt . Println ()
i ++
goto Here
}for loop有三種形式,其中只有一種具有分號: for init ; condition ; post { } // aloop using the syntax borrowed from C
for condition { } // a while loop
for { } // a endless loop
sum := 0
for i := 0 ; i < 10 ; i ++ {
sum = sum + i
} for i := 0 ; i < 10 ; i ++ {
if i > 5 {
break
}
fmt . Println ( i )
}
/* With loops within loop you can specify a label after `break` to identify which loop to stop */
J: for j := 0 ; j < 5 ; j ++ {
for i := 0 ; i < 10 ; i ++ {
if i > 5 {
break J
}
fmt . Println ( i )
}
}範圍:
range可用於循環。它可以在切片,陣列,字符串,地圖和通道上循環。range是一個迭代器,當調用時,它會返回“事物”循環的下一個鍵值對。 list := [] string { "a" , "b" , "c" , "d" , "e" , "f" }
for k , v := range list {
// do some fancy thing with k & v
}轉變:
switch沒有表達式,則它將打開true 。if-else-if-else鏈寫為switch 。 /* Convert hexadecimal character to an int value */
switch { // switch without condition = switch true
case '0' <= c && c <= '9' :
return c - '0'
case 'a' <= c && c <= 'f' :
return c - 'a' + 10
case 'A' <= c && c <= 'F' :
return c - 'A' + 10
}
return 0
/* Automatic fall through */
switch i {
case 0 : fallthrough
case 1 :
f ()
default :
g ()
} close new panic complex
delete make recover real
len append print imag
cap copy println
len用於返回Strring,地圖,切片和數組的長度。copy是用於復制切片。 append是用於串聯切片。簡介:列表 - >數組,切片。 dict->地圖
數組:
[n]<type>定義。 var arr [ 10 ] int // The size is part of the type, fixed size
arr [ 0 ] = 42
arr [ 1 ] = 13
fmt . Printf ( "The first element is %s n " , arr [ 0 ])
// Initialize an array to something other than zero, using composite literal
a := [ 3 ] int { 1 , 2 , 3 }
a := [ ... ] int { 1 , 2 , 3 }切片:
// Init array primes
primes := [ 6 ] int { 2 , 3 , 5 , 7 , 11 , 13 }
// Init slice s
var s [] int = primes [ 1 : 4 ]
fmt . Println ( s ) // Return [3, 5, 7]
/* slice_length_capacity.go */
package main
import "fmt"
func main () {
s := [] int { 2 , 3 , 5 , 7 , 11 , 13 }
printSlice ( s )
// Slice the slice to give it zero length.
s = s [: 0 ]
printSlice ( s )
// Extend its length.
s = s [: 4 ]
printSlice ( s )
// Drop its first two values.
s = s [ 2 :]
printSlice ( s )
}
func printSlice ( s [] int ) {
fmt . Printf ( "len=%d cap=%d %v n " , len ( s ), cap ( s ), s )
} s := make ([] byte , 5 ) len是切片所指的元素數量。
cap是基礎數組中的元素數量(從切片指針提到的元素開始)。
s = s [ 2 : 4 ]切片不會復制切片的數據。它創建了一個指向原始數組的新切片。這使得切片操作效率與操縱陣列所指示的效率一樣高。因此,修改重新佈置的元素(不是切片本身)會修改原始切片的元素:
d := [] byte { 'r' , 'o' , 'a' , 'd' }
e := d [ 2 :]
// e = []byte{'a', 'd'}
e [ 1 ] = 'm'
// e = []byte{'a', 'm'}
// d = []byte{'r', 'o', 'a', 'm'}s切成比其容量短的長度。我們可以通過再次將其切成薄片來增強其能力。 s = s [: cap ( s )]切片不能超出其能力。
// Another example
var array [ m ] int
slice := array [: n ]
// len(slice) == n
// cap(slice) == m
// len(array) == cap(array) == mappend和copy 。 s0 := [] int { 0 , 0 }
s1 := append ( s0 , 2 ) // same type as s0 - int.
// If the original slice isn't big enough to fit the added values,
// append will allocate a new slice that is big enough. So the slice
// returned by append may refer to a different underlaying array than
// the original slices does.
s2 := append ( s1 , 3 , 5 , 7 )
s3 := append ( s2 , s0 ... ) // []int{0, 0, 2, 3, 5, 7, 0, 0} - three dots used after s0 is needed make it clear explicit that you're appending another slice, instead of a single value
var a = [ ... ] int { 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 }
var s = make ([] int , 6 )
// copy function copies slice elements from source to a destination
// returns the number of elements it copied
n1 := copy ( s , a [ 0 :]) // n1 = 6; s := []int{0, 1, 2, 3, 4, 5}
n2 := copy ( s , s [ 2 :]) // n2 = 4; s := []int{2, 3, 4, 5, 4, 5}地圖:
map類型。 monthdays := map [ string ] int {
"Jan" : 31 , "Feb" : 28 , "Mar" : 31 ,
"Apr" : 30 , "May" : 31 , "Jun" : 30 ,
"Jul" : 31 , "Aug" : 31 , "Sep" : 30 ,
"Oct" : 31 , "Nov" : 30 , "Dec" : 31 , // A trailing comma is required
}
value , key := monthdays [ "Jan" ]make 。地圖是參考類型。地圖不是參考變量,其值是指向runtime.hmap結構的指針。 // A struct is a type. It's also a collection of fields
// Declaration
type Vertex struct {
X , Y float64
}
// Creating
var v = Vertex { 1 , 2 }
var v = Vertex { X : 1 , Y : 2 } // Creates a struct by defining values with keys
var v = [] Vertex {{ 1 , 2 },{ 5 , 2 },{ 5 , 5 }} // Initialize a slice of structs
// Accessing members
v . X = 4
// You can declare methods on structs. The struct you want to declare the
// method on (the receiving type) comes between the the func keyword and
// the method name. The struct is copied on each method call(!)
func ( v Vertex ) Abs () float64 {
return math . Sqrt ( v . X * v . X + v . Y * v . Y )
}
// Call method
v . Abs ()
// For mutating methods, you need to use a pointer (see below) to the Struct
// as the type. With this, the struct value is not copied for the method call.
func ( v * Vertex ) add ( n float64 ) {
v . X += n
v . Y += n
}map[string]intefaces 。 // ReadWriter implementations must satisfy both Reader and Writer
type ReadWriter interface {
Reader
Writer
}
// Server exposes all the methods that Logger has
type Server struct {
Host string
Port int
* log. Logger
}
// initialize the embedded type the usual way
server := & Server { "localhost" , 80 , log. New ( ... )}
// methods implemented on the embedded struct are passed through
server. Log ( ... ) // calls server.Logger.Log(...)
// the field name of the embedded type is its type name (in this case Logger)
var logger * log. Logger = server. Logger // General Function
type mytype int
func ( p mytype ) funcname ( q , int ) ( r , s int ) { return 0 , 0 }
// p (optional) bind to a specific type called receiver (a function with a receiver is usually called a method)
// q - input parameter
// r,s - return parameters import "fmt"
func main () {
a := func () { // a is defined as an anonymous (nameless) function,
fmt . Println ( "Hello" )
}
a ()
} func printit ( x int ) {
fmt . Println ( "%v n " , x )
}
func callback ( y int , f func ( int )) {
f ( y )
} /* Open a file & perform various writes & reads on it. */
func ReadWrite () bool {
file . Open ( "file" )
// Do your thing
if failureX {
file . Close ()
return false
}
// Repeat a lot of code.
if failureY {
file . Close ()
return false
}
file . Close ()
return true
}
/* Same situation but using defer */
func ReadWrite () bool {
file . Open ( "file" )
defer file . Close () // add file.Close() to the defer list
// Do your thing
if failureX {
return false
}
if failureY {
return false
}
return true
}Defer函數按LIFO順序執行。 for i := 0 ; i < 5 ; i ++ {
defer fmt . Printf ( "%d " , i ) // 4 3 2 1 0
}defer您甚至可以更改返回值,前提是您使用命名的結果參數和函數字面的( def func(x int) {/*....*/}(5) )。 func f () ( ret int )
defer func () { // Initialized with zero
ret ++
}()
return 0 // This will not be the returned value, because of defer. Ths function f will return 1
} func func1 ( arg ... int ) { // the variadic parameter is just a slice.
for _ , n := range arg {
fmt . Printf ( "And the number is: %d n " , n )
}
}pacnic時,執行F停止時,F型中的任何延遲功能均正常執行,然後F返回其呼叫者。對於來電者,F然後表現得像恐慌。該過程繼續堆疊,直到當前Goroutine中的所有功能都返回,此時程序崩潰了。恐慌可以通過直接引起恐慌來發起。它們也可能是由運行時錯誤引起的,例如越野陣列訪問。 /* defer_panic_recover.go */
package main
import "fmt"
func main () {
f ()
fmt . Println ( "Returned normally from f." )
}
func f () {
defer func () {
if r := recover (); r != nil {
fmt . Println ( "Recovered in f" , r )
}
}()
fmt . Println ( "Calling g." )
g ( 0 )
fmt . Println ( "Returned normally from g." )
}
func g ( i int ) {
if i > 3 {
fmt . Println ( "Panicking!" )
panic ( fmt . Sprintf ( "%v" , i ))
}
defer fmt . Println ( "Defer in g" , i )
fmt . Println ( "Printing in g" , i )
g ( i + 1 )
}
/* Result */
// Calling g.
// Printing in g 0
// Printing in g 1
// Printing in g 2
// Printing in g 3
// Panicking!
// Defer in g 3
// Defer in g 2
// Defer in g 1
// Defer in g 0
// Recovered in f 4
// Returned normally from f. package even
func Even ( i int ) bool { // starts with capital -> exported
return i % 2 == 0
}
func odd ( i int ) bool { // start with lower-case -> private
return i % 2 == 1
}mkdir $GOPATH /src/even
cp even.go $GOPATH /src/even
go build
go installimport "even"中的程序中的包裹使用。import bar "bytes" 。src/compress/gzip中的軟件包被導入為compress/gzip但具有name gzip ,而不是compress/gzip 。ring.Ring package的新實例的功能( container/ring )通常稱為NewRing ,但是由於Ring是該軟件包導出的唯一類型,因為軟件包被稱為ring ,因此稱為New 。包裝的客戶將其視為ring.New 。doc.go 。僅包含包裹註釋的文檔。 /*
The regexp package implements a simple library for
regular expressions.
The syntax of the regular expressions accepted is:
regexp:
concatenation { '|' concatenation }
*/
package regexpmain函數的文件是輸入文件進行執行。main函數一樣,初始化軟件包時,go調用init函數。它不需要任何參數,也不會返回任何值。 init函數被隱含地聲明。您可以在文件或軟件包中具有多個init功能。在文件中執行init函數的順序將按照其外觀順序。null容器。go run * .go
├── Main package is executed
├── All imported packages are initialized
| ├── All imported packages are initialized (recursive definition)
| ├── All global variables are initialized
| └── init functions are called in lexical file name order
└── Main package is initialized
├── All global variables are initialized
└── init functions are called in lexical file name order安裝第三方軟件包只是將遠程代碼克隆到本地src/<package>目錄中。不幸的是,GO不支持軟件包版本或提供軟件包管理器,但建議在這裡等待。
testing軟件包和程序go test 。 package main
func Sum ( x int , y int ) int {
return x + y
}
func main () {
Sum ( 5 , 5 )
}
// Testcase
package main
import "testing"
func TestSum ( t * testing. T ) {
total := Sum ( 5 , 5 )
if total != 10 {
t . Errorf ( "Sum was incorrect, got: %d, want: %d." , total , 10 )
}
} package main
import "testing"
func TestSum ( t * testing. T ) {
tables := [] struct {
x int
y int
n int
}{
{ 1 , 1 , 2 },
{ 1 , 2 , 3 },
{ 2 , 2 , 4 },
{ 5 , 2 , 7 },
}
for _ , table := range tables {
total := Sum ( table . x , table . y )
if total != table . n {
t . Errorf ( "Sum of (%d+%d) was incorrect, got: %d, want: %d." , table . x , table . y , total , table . n )
}
}
}啟動測試:
go testgo test github.com/username/packageHTTP測試:
net/http/httptest子包裝促進了HTTP服務器和客戶端代碼的測試自動化。httptest.ResponseRecorder專門旨在通過檢查測試函數中的HTTP.ResponseWriter的狀態更改,以提供單位測試功能來行使HTTP處理程序方法。httptest提供類型httptest.Server以編程方式創建服務器來測試客戶端請求並將模擬響應發送給客戶端。語句覆蓋範圍: go test工具具有用於語句的內置代碼覆蓋。
$ go test -cover
PASS
coverage: 50.0% of statements
ok github.com/alexellis/golangbasics1 0.009s
# Generate a HTML coverage report.
$ go test -cover -coverprofile=c.out
$ go tool cover -html=c.out -o coverage.html代碼基準:基準測試的目的是測量代碼的性能。 GO測試命令行工具具有支持基準指標的自動生成和測量的支持。與單元測試相似,測試工具使用基準功能來指定要測量的代碼的哪些部分。
運行基準
$ > go test -bench=.
PASS
BenchmarkVectorAdd-2 2000000 761 ns/op
BenchmarkVectorSub-2 2000000 788 ns/op
BenchmarkVectorScale-2 5000000 269 ns/op
BenchmarkVectorMag-2 5000000 243 ns/op
BenchmarkVectorUnit-2 3000000 507 ns/op
BenchmarkVectorDotProd-2 3000000 549 ns/op
BenchmarkVectorAngle-2 2000000 659 ns/op
ok github.com/vladimirvivien/learning-go/ch12/vector 14.123s跳過測試功能
> go test -bench=. -run=NONE -v
PASS
BenchmarkVectorAdd-2 2000000 791 ns/op
BenchmarkVectorSub-2 2000000 777 ns/op
Code Testing
[ 314 ]
...
BenchmarkVectorAngle-2 2000000 653 ns/op
ok github.com/vladimirvivien/learning-go/ch12/vector 14.069s比較基準:比較實現相似功能的不同算法的性能。使用性能基準測試算法將表明哪些實現可能更加計算和內存效率。
隔離依賴關係:定義單位測試的關鍵因素是與運行時依賴關係或協作者隔離。檢查依賴注入。
FMT :軟件包fmt通過類似於C的printf & scanf功能實現了格式的I/O。格式動詞源自C,但更簡單。一些可以使用的動詞(%序列):
IO :該軟件包為I/O原始詞提供了基本接口。它的主要工作是將此類原語的現有實現(例如包裝os中的原始)包含在抽像功能的共享公共接口中,以及其他一些相關的原始界面。
Bufio :此軟件包實現了緩衝的I/O。它包含一個io.reader或io.Writer對象,創建另一個對象(讀取器或作者),該對像也實現了接口,但為文本I/O提供了緩沖和一些幫助。
排序:排序軟件包提供了用於分類數組和用戶定義集合的原始內容。
STRCONV :STRCONV軟件包實現了基本數據類型的字符串表示的轉換。
OS :OS軟件包為操作系統功能提供了獨立於平台的接口。設計類似於Unix。
同步:軟件包同步提供了基本的同步基原始人,例如相互排除鎖。
標誌:標誌軟件包實現命令行標誌解析。
編碼/JSON :編碼/JSON軟件包實現了RFC 4627中定義的JSON對象的編碼和解碼。
HTML/模板:用於生成文本輸出(例如HTML)的數據驅動模板。
NET/HTTP :NET/HTTP軟件包實現HTTP請求,答復和URLS&提供可擴展的HTTP服務器和基本HTTP客戶端的解析。
不安全:不安全的軟件包包含圍繞GO程序類型安全的操作。通常,您不需要此軟件包,但是值得一提的是不安全的GO程序。
反思:反射軟件包實現運行時反射,允許程序用任意類型來操縱對象。典型的用途是使用靜態類型接口{}的值,並通過調用TypeOf提取其動態類型信息,該typeof返回具有接口類型的對象。
OS/EXEC :OS/EXEC軟件包運行外部命令。
Go有指針,但沒有指針,因此它們的作用更像是參考,而不是您從C中知道的指針。
var p * int
p ++this 。指針很有用。請記住,當您在GO中調用函數時,變量是逐個價值的。因此,為了效率和修改傳遞值的功能的可能性,我們有指示。
指針類型( *類型)和地址的運算符 *:如果將變量聲明為var x int ,則表達式&x (x of x of x of x of x int)會產生一個指向整數變量的指針(類型* int的值)。如果此值稱為p ,我們說“ p向x ”或等效地“ p包含x的地址”。 p點寫的變量*p 。表達式*p產生該變量的值,一個int ,但由於*p表示變量,因此它也可能出現在分配的左側,在這種情況下,分配會更新變量。參考
x := 1
p := & x // p, of type *int, points to x
fmt . Println ( * p ) // "1"
* p = 2 // equivalent to x = 2
fmt . Println ( x ) // "2"所有新聲明的變量均分配了其零值,而指示也沒有什麼不同。新宣布的指針,或者只是指向一無所有的指針具有尼爾值。
var p * int // declare a pointer
fmt . Printf ( "%v" , p )
var i int
p = & i // Make p point to i
fmt . Printf ( "%v" , p ) // Print somthing like 0x7ff96b81c000a // Go program to illustrate the
// concept of the Pointer to Pointer
package main
import "fmt"
// Main Function
func main () {
// taking a variable
// of integer type
var V int = 100
// taking a pointer
// of integer type
var pt1 * int = & V
// taking pointer to
// pointer to pt1
// storing the address
// of pt1 into pt2
var pt2 * * int = & pt1
fmt . Println ( "The Value of Variable V is = " , V )
fmt . Println ( "Address of variable V is = " , & V )
fmt . Println ( "The Value of pt1 is = " , pt1 )
fmt . Println ( "Address of pt1 is = " , & pt1 )
fmt . Println ( "The value of pt2 is = " , pt2 )
// Dereferencing the
// pointer to pointer
fmt . Println ( "Value at the address of pt2 is or *pt2 = " , * pt2 )
// double pointer will give the value of variable V
fmt . Println ( "*(Value at the address of pt2 is) or **pt2 = " , * * pt2 )
}
// The Value of Variable V is = 100
// Address of variable V is = 0x414020
// The Value of pt1 is = 0x414020
// Address of pt1 is = 0x40c128
// The value of pt2 is = 0x40c128
// Value at the address of pt2 is or *pt2 = 0x414020
// *(Value at the address of pt2 is) or **pt2 = 100Go也有垃圾收集。
為了分配記憶,GO有2個原始詞, new & make 。
新分配;初始化。
構造者和compiste文字
// A lot of boiler plate
func NewFile ( fd int , name string ) * File {
if fd < 0 {
return nil
}
f := new ( File )
f . fd = fd
f . name = name
f . dirinfo = nil
f . nepipe = 0
return f
}
// Using a composite literal
func NewFile ( fd int , name string ) * File {
if fd < 0 {
return nil
}
f := File { fd , name , nil . 0 }
return & f // Return the address of a local variable. The storage associated with the variable survives after the function returns.
// return &File{fd, name, nil, 0}
// return &File{fd: fd, name: name}
}作為限制情況,如果復合文字根本不包含字段,則它將為該類型創建零值。表達式new(File) && &File{}是等效的。
還可以為陣列,切片和地圖創建複合文字,而字段標籤是指數或適當的地圖鍵。
ar := [ ... ] string { Enone : "no error" , Einval : "invalid argument" }
sl := [] string { Enone : "no error" , Einval : "invalid argument" }
ma := map [ int ] string { Enone : "no error" , Einval : "invalid argument" } /* defining_own_type.go */
package main
import "fmt"
type NameAge struct {
name string // both non exported fiedls
age int
}
func main () {
a := new ( NameAge )
a . name = "Kien"
a . age = 25
fmt . Printf ( "%v n " , a ) // &{Kien, 25}
} struct {
x , y int
A * [] int
F func ()
}方法:
func doSomething1 ( n1 * NameAge , n2 int ) { /* */ }
// method call
var n * NameAge
n . doSomething1 ( 2 ) func ( n1 * NameAge ) doSomething2 ( n2 int ) { /* */ }注意:如果x是可尋址&x的方法集,則包含m,xm()是(&x).m()的速記。
// A mutex is a data type with two methods, Lock & Unlock
type Mutex struct { /* Mutex fields */ }
func ( m * Mutex ) Lock () { /* Lock impl */ }
func ( m * Mutext ) Unlock { /* Unlock impl */ }
// NewMutex is equal to Mutex, but it does not have any of the methods of Mutex.
type NewMutex Mutex
// PrintableMutex hash inherited the method set from Mutex, contains the methods
// Lock & Unlock bound to its anonymous field Mutex
type PrintableMutex struct { Mutex } FROM b []byte i []int r []rune s string f float32 i int
TO
[]byte . []byte(s)
[]int . []int(s)
[]rune []rune(s)
string string(b) string(i) string(r) .
float32 . float32(i)
int int(f) .
string到字節或符文 mystring := "hello this is string"
byteslice := [] byte ( mystring )
runeslice := [] rune ( string ) b := [] byte { 'h' , 'e' , 'l' , 'l' , 'o' } // Composite literal
s := string ( b )
i := [] rune ( 26 , 9 , 1994 )
r := string ( i )對於數字值:
uint8(int) 。int(float32) 。這會從浮點值中丟棄分數部分。float32(int)其他方式用戶定義的類型和轉換
type foo struct { int } // Anonymous struct field
type bar foo // bar is an alias for foo
var b bar = bar { 1 } // Declare `b` to be a `bar`
var f foo = b // Assign `b` to `f` --> Cannot use b (type bar) as type foo in assignment
var f foo = foo ( b ) // OK! /* a struct type S with 1 field, 2 methods */
type S struct { i int }
func ( p * S ) Get () int { return p . i }
func ( p * S ) Put ( v int ) { p . i = v }
/* an interface type */
type I interface {
Get () int
Put () int
}
/* S is a valid implementation for interface I */ func f ( p I ) {
fmt . Println ( p . Get ())
p . Put ( 1 )
}
var s S
/* Because S implements I, we can call the
function f passing in a pointer to a value
of type S */
/* The reason we need to take the address of s,
rather than a value of type S, is because
we defined the methods on s to operae on pointers */
f ( & s )您不需要聲明某種類型是否實現接口的事實意味著可以實現鴨子打字的形式。這不是純鴨打字,因為在可能的情況下,GO complier會靜態檢查該類型是否實現了地面。但是,GO確實具有純粹的動態方面,因為您可以將一個接口轉換為另一個接口。在一般情況下,該轉換在運行時進行檢查。如果轉換無效 - 如果存儲在現有接口值中的值的類型無法滿足其要轉換的接口,則該程序將在運行時錯誤中失敗。
package main
import "fmt"
type Duck interface {
Quack ()
}
type Donald struct {
}
func ( d Donald ) Quack () {
fmt . Println ( "quack quack!" )
}
type Daisy struct {
}
func ( d Daisy ) Quack () {
fmt . Println ( "-quack -quack" )
}
func sayQuack ( duck Duck ) {
duck . Quack ()
}
type Dog struct {
}
func ( d Dog ) Bark () {
fmt . Println ( "go go" )
}
func main () {
donald := Donald {}
sayQuack ( donald ) // quack
daisy := Daisy {}
sayQuack ( daisy ) // --quack
dog := Dog ()
sayQuack ( dog ) // compile error - cannot use dog (type Dog) as type Duck
} GO的接口讓您像純動態語言一樣使用duck typing ,但仍有編譯器捕獲明顯的錯誤,例如傳遞帶有Read方法的對象的int ,或者像以錯誤數量的參數調用Read方法一樣。
type R struct { i int }
func ( p * R ) Get () int { return p . i }
func ( p * R ) Put ( v int ) { p . i = v }
func f ( p I ) {
switch t := p .( type ) {
case * S :
case * R :
default :
}
} func g ( something interface {}) int {
return something .( I ). Get ()
}.(I)是一種類型的斷言,它將something轉換為類型I的接口。如果我們有類型,則可以調用Get()函數。 s = new ( S )
fmt . Println ( g ( s ))方法是具有接收器的函數。
您可以在任何類型上定義方法(非本地類型除外,這包括內置類型:類型int不能具有方法)。
接口類型的方法
按照慣例,一個方法名稱和-er後綴命名:讀者,作者,格式化,...
指針和非點方法接收器。
func ( s * MyStruct ) pointerMethod () {} // method on pointer
func ( s MyStruct ) valueMethod () {} // method on value package main
import "fmt"
type Mutatable struct {
a int
b int
}
func ( m Mutatable ) StayTheSame () {
m . a = 5
m . b = 7
}
func ( m * Mutatable ) Mutate () {
m . a = 5
m . b = 7
}
func main () {
m := & Mutatable { 0 , 0 }
fmt . Println ( m )
m . StayTheSame ()
fmt . Println ( m )
m . Mutate ()
fmt . Println ( m )
}struct ,則使用指針接收器會便宜得多。struct等類型,值接收器非常便宜,因此除非方法的語義需要指針,否則值接收器是有效且清晰的。 package main
import "fmt"
func do ( i interface {}) {
switch v := i .( type ) {
case int :
fmt . Printf ( "Twice %v is %v n " , v , v * 2 )
case string :
fmt . Printf ( "%q is %v bytes long n " , v , len ( v ))
default :
fmt . Printf ( "I don't know about type %T! n " , v )
}
}
func main () {
do ( 21 )
do ( "hello" )
do ( true )
}
// Twice 21 is 42
// "hello" is 5 bytes long
// I don't know about type bool! ready ( "Tea" , 2 ) // Normal function call
go ready ( "Tea" , 2 ) // .. Bum! Here is goroutine
/* X ready example */
package main
import (
"fmt"
"time"
)
func ready ( w string , sec int ) {
time . Sleep ( time . Duration ( sec ) * time . Second )
fmt . Println ( w , "is ready!" )
}
func main () {
go ready ( "Tea" , 2 ) // Tea is ready - After 2 second (3)
go ready ( "Coffee" , 1 ) // Coffee is ready - After 1 second (2)
fmt . Println ( "I'm waiting" ) // Right away (1)
// If did not wait for the goroutines, the program would be terminated
// immediately & any running goroutines would die with it!
time . Sleep ( 5 * time . Second )
} /* Define a channel, we must also define the type of
the values we can send on the channel */
ci := make ( chan int )
cs := make ( chan string )
cf := make ( chan interface {})
ci <- 1 // Send the integer 1 to the channel ci
<- ci // Receive an integer from the channel ci
i := <- ci // Receive from the channel ci & store it in i package main
import (
"fmt"
"time"
)
var c chan int
func ready ( w string , sec int ) {
time . Sleep ( time . Duration ( sec ) * time . Second )
fmt . Println ( w , "is ready!" )
c <- 1
}
func main () {
c = make ( chan int )
go ready ( "Tea" , 2 )
go ready ( "Coffee" , 1 )
fmt . Println ( "I'm waiting" )
<- c // Wait until we receive a value from the channel
<- c
}緩衝通道:
緩衝通道具有容量。
緩衝通道用於執行異步通訊。
緩衝渠道沒有這樣的保證。
僅當頻道中沒有值的值時,接收才會阻止。
僅當沒有可用的緩衝區以放置要發送的值時,發送才會阻止。
未封閉的頻道:
未封閉的渠道沒有能力,因此需要兩個goroutines準備進行任何交換。
未封閉的通道用於在Goroutines之間執行同步通信。未封閉的通道提供了保證,即在發送和接收的瞬間進行2個goroutines之間的交換。
同步是在通道上發送和接收之間的相互作用的基礎。
unbuffered := make ( chan int ) // Unbuffered channel of integer type
buffered := make ( chan int , 10 ) // Buffered channel of integer typeselect 。 L: for {
select {
case <- c :
i ++
if i > 1 {
break L
}
}
} package main
import "fmt"
func fibonacci ( c , quit chan int ) {
x , y := 0 , 1
for {
select {
case c <- x :
x , y = y , x + y
case <- quit :
fmt . Println ( "quit" )
return
}
}
}
func main () {
c := make ( chan int )
quit := make ( chan int )
go func () {
for i := 0 ; i < 10 ; i ++ {
fmt . Println ( <- c )
}
quit <- 0
}()
fibonacci ( c , quit )
}
// 2
// 3
// 5
// 8
// 13
// 21
// 34
// quit // Default selection
// The default case in a select is run if no other case is ready.
// Use a default case to try a send or receive without blocking:
// select {
// case i := <-c:
// // use i
// default:
// // receiving from c would block
// }
package main
import (
"fmt"
"time"
)
func main () {
tick := time . Tick ( 100 * time . Millisecond )
boom := time . After ( 500 * time . Millisecond )
for {
select {
case <- tick :
fmt . Println ( "tick." )
case <- boom :
fmt . Println ( "BOOM!" )
return
default :
fmt . Println ( " ." )
time . Sleep ( 50 * time . Millisecond )
}
}
}當我們的goroutines同時運行時,它們沒有並行運行! (再過一次,請確保您知道並發不是Parral的!)
使用runtime.GOMAXPROCS(n)或設置環境變量GOMAXPROCS您可以設置可以並行運行的Goroutines數量。
從版本1.5及以上, GOMAXPROCS默認為CPU內核數。
ch := make ( chan type , value )
// if value == 0 -> unbuffered
// if value > 0 -> buffer value elements x , ok = <- ch
/ * Where ok is set to True the channel is not closed & we 'v e read something
Otherwise ok is set to False . In that case the channel was closed & the value
received is a zero value of the channel 's type .io.Reader & io.Writer接口。io.Reader是該語言中的重要界面。需要從某些內容中讀取的功能(如果不是全部)功能,以io.Reader為輸入。io.Writer具有Write方法。io.Reader或io.Writer界面,則可以在該類型上使用整個標準GO庫。os.Args內部提供。flag軟件包具有更複雜的接口,還提供了一種解析標誌的方法。os/exec軟件包具有運行外部命令的功能,並且是從GO程序中執行命令的首要方法。 import "os/exec"
cmd := exec . Command ( "/bin/ls" , "-l" )
// Just run without doing anything with the returned data
err := cmd . Run ()
// Capturing the standard output
buf , err := cmd . Output () // buf is byte slicenet中找到。Dial 。當您Dial遠程系統時,功能返回Conn接口類型,可用於發送和接收信息。函數Dial整齊地抽象了網絡家族和運輸。 conn , e := Dial ( "tcp" , "192.0.32.10:80" )
conn , e := Dial ( "udp" , "192.0.32.10:80" )
conn , e := Dial ( "tcp" , "[2620:0:2d0:200::10]:80" )GO 1.11包括對模塊的初步支持,GO GO的新依賴關係管理系統,該系統使依賴版本信息明確且更易於管理。
go.mod文件的GO源文件樹定義。模塊源代碼可能位於Gopath之外。有四個指令: module , require , replace , exclude 。require尚未被啟用go.mod ,諸如“ Go Build”和“ Go Go test”之類的大多數GO命令都會自動查找正確的模塊,並將該新的直接依賴性的最高require添加到模塊的go.mod 。例如,如果您的新導入對應於最新標記的發行版本為v1.2.3的依賴項M,則您的模塊的go.mod最終將獲得require M v1.2.3 ,這表明模塊M是允許版本> = v1.2.3(and <v2,給定的V2,v2與V1不相容)。v1.2.3之類的標籤)。go.mod文件中使用的模塊路徑末端的模塊的主要版本作為A /vN包含(例如, module github.com/my/mod/v2 , require github.com/my/mod/v2 v2.0.0 )和package import import import(eg, import "github.com/my/mod/v2/mypkg" 。go.mod $GOPATH/src使用模塊使用。 (在$GOPATH/src內部,為了兼容,即使找到了go.mod ,GO命令仍在舊的GOPATH模式下運行) # Create a directory outside of your GOPATH:
$ mkdir -p /tmp/scratchpad/hello
$ cd /tmp/scratchpad/hello
# Initialize a new module:
$ go mod init github.com/you/hello
go: creating new go.mod: module github.com/you/hello
# Write your code
$ cat << EOF > hello.go
package main
import (
"fmt"
"rsc.io/quote"
)
func main() {
fmt.Println(quote.Hello())
}
EOF
# Introduce `go mod tidy`
# Tidy makes sure go.mod matches the source code in the module.
# It adds any missing modules necessary to build the current module's
# packages and dependencies, and it removes unused modules that
# don't provide any relevant packages. It also adds any missing entries
# to go.sum and removes any unnecessary ones
$ go mod tidy t/s/hello ﳑ
go: finding module for package rsc.io/quote
go: downloading rsc.io/quote v1.5.2
go: found rsc.io/quote in rsc.io/quote v1.5.2
go: downloading rsc.io/sampler v1.3.0
go: downloading golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
$ cat go.mod
module github.com/you/hello
require rsc.io/quote v1.5.2
# Add a new dependency often brings in other indirect dependencies too
# List the current module and all its dependencies
$ go list -m all
github.com/you/hello
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0
# In addition to go.mod, there is a go.sum file containing the expected
# cryptographic hashes of the content of specific module versions
$ cat go.sum t/s/hello ﳑ
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZOaTkIIMiVjBQcw93ERBE4m30iBm00nkL0i8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y=
rsc.io/quote v1.5.2/go.mod h1:LzX7hefJvL54yjefDEDHNONDjII0t9xZLPXsUe+TKr0=
rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
# Build & run
$ go build
$ ./hello
Hello, world. # From the output of go list -m all, we're using an untagged version of golang.org/x/text
# Let's upgrade to the latest tagged version
$ go get golang.org/x/text t/s/hello ﳑ
go: downloading golang.org/x/text v0.7.0
go: upgraded golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c = > v0.7.0
$ cat go.mod t/s/hello ﳑ
module github.com/you/hello
go 1.19
require rsc.io/quote v1.5.2
require (
golang.org/x/text v0.7.0 // indirect
rsc.io/sampler v1.3.0 // indirect
)
$ go list -m all t/s/hello ﳑ
github.com/you/hello
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f
golang.org/x/text v0.7.0
golang.org/x/tools v0.1.12
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0go mod tidy ,然後去做其餘的。go Tool提供go mod vendor命令的原因。go mod vendor命令構造一個名為vendor的目錄在主模塊的根目錄中,其中包含支持主模塊中支持構建和測試的所有軟件包的副本。go mod vendor還創建了文件vendor/modules.txt ,其中包含供應商軟件包列表及其複制的模塊版本。vendor ,然後將其複制。 $ go mod vendor
# Main module's directory structure
$ tree -L 3
├── go.mod
├── go.sum
├── hello.go
└── vendor
├── golang.org
│ └── x
├── modules.txt
└── rsc.io
├── quote
└── samplergo工具默認設置到從公共GO模塊鏡像下載模塊:https://proxy.golang.org,還默認可用於驗證下載的模塊(無論源)對公共GO檢查數據庫的https://sum.golang.org.org。 export GOPROXY=https://goproxy.io,directgo命令默認將從公共GO模塊鏡像下載模塊,因此,如果您有私有代碼,則很可能應該配置GOPRIVATE設置(例如go env -w GOPRIVATE=*.corp.com,github.com/secret/repo ),或更精細的變體的GONOPROXY或GONOSUMDB ,以支持頻率更低。有關更多詳細信息,請參見文檔。go.work的新文件共享依賴關係的常見列表。該文件中的依賴項可以跨越多個模塊,並且在go.work中聲明的所有模塊。 Work文件將覆蓋模塊的go.mod中的依賴項。go.work文件中聲明工作空間,該文件指定了工作空間中每個模塊的模塊目錄的相對路徑。當go.work存在時,工作空間由包含當前目錄的單個模塊組成。go.work中的詞彙元素。工作文件的定義與go.mod文件完全相同。 go 1.18
use . / my / first / thing
use . / my / second / thing
// or
// use (
// ./my/first/thing
// ./my/second/thing
// )
replace example . com / bad / thing v1 .4 .5 = > example . com / good / thing v1 .4 .5$ mkdir workspace
$ cd workspace
# Create hello module
$ mkdir hello
$ cd hello
$ go mod init eaxmple.com/hello
go: creating new go.mod: module example.com/hello
$ cat << EOF > hello.go
package main
import (
"fmt"
"golang.org/x/example/stringutil"
)
func main() {
fmt.Println(stringutil.Reverse("Hello"))
}
EOF
$ go mod tidy
go: finding module for package golang.org/x/example/stringutil
go: found golang.org/x/example/stringutil in golang.org/x/example v0.0.0-20220412213650-2e68773dfca0
$ go run example.com/hello
olleH
# Create the workspace
$ cd ../
$ go work init ./hello
$ tree
.
├── go.work
└── hello
├── go.mod
├── go.sum
└── hello.go
1 directory, 4 files
# Go command includes all the modules in the workspace as main modules. This allow us to refer to a package in the module
# even outside the module.
$ go run example.com/hello
olleH
# Download and modify the golang.org/x/example module
$ git clone https://go.googlesource.com/example
Cloning into ' example ' ...
remote: Total 165 (delta 27), reused 165 (delta 27)
Receiving objects: 100% (165/165), 434.18 KiB | 1022.00 KiB/s, done.
Resolving deltas: 100% (27/27), done.
# Add module to the workspace
$ go work use ./example
$ tree -L 1
.
├── example
├── go.work
└── hello
2 directories, 1 file
$ cat go.work
go 1.20
use (
./example
./hello
)
$ cd example/stringutil
# Create a new file
$ cat << EOF > toupper.go
package stringutil
import "unicode"
// ToUpper uppercases all the runes in its argument string.
func ToUpper(s string) string {
r := []rune(s)
for i := range r {
r[i] = unicode.ToUpper(r[i])
}
return string(r)
}
EOF
# Modify hello program
$ cd ../../hello
$ cat << EOF > hello.go
package main
import (
"fmt"
"golang.org/x/example/stringutil"
)
func main() {
fmt.Println(stringutil.ToUpper("Hello"))
}
EOF
$ cd ..
# Go command finds the example.com/hello module specified in the command line
# in the hello directory specified by the go.work file, and similiarly
# resolves the golang.org/x/example import using the go.work file.
$ go run example.com/hello
HELLO來源:https://go.dev/doc/modules/layout
GO項目可以包括軟件包,命令行計劃或兩者的組合。本指南是按項目類型組織的。
注意:在整個文檔中,文件/軟件包名稱是完全任意的
project-root-directory/
go.mod
modname.go
modname_test.go
auth.go
auth_test.go
hash.go
hash_test.gomodname.go中的代碼以: package modname
// ... package code here project-root-directory/
go.mod
auth.go
auth_test.go
client.go
main.gomain.go文件包含func main ,但這只是一個約定。 “主”文件也可以稱為modname.go (對於適當的modName值)或其他任何內容。 internal目錄中;這樣可以防止其他模塊依賴於包裝,我們不一定要揭露和支持外部用途。由於其他項目無法從我們的internal目錄導入代碼,因此我們可以自由重構其API,並且通常不破壞外部用戶而移動。因此,包裝的項目結構是: project-root-directory/
internal/
auth/
auth.go
auth_test.go
hash/
hash.go
hash_test.go
go.mod
modname.go
modname_test.goproject-root-directory/
go.mod
modname.go
modname_test.go
auth/
auth.go
auth_test.go
token/
token.go
token_test.go
hash/
hash.go
internal/
trace/
trace.go module github . com / someuser / modname project-root-directory/
go.mod
internal/
... shared internal packages
prog1/
main.go
prog2/
main.gomain 。頂級internal目錄可以包含存儲庫中所有命令使用的共享軟件包。cmd目錄中。儘管僅在僅由命令組成的存儲庫中嚴格必要,但它在具有命令和可導入軟件包的混合存儲庫中非常有用,正如我們接下來將討論的那樣。 project-root-directory/
go.mod
modname.go
modname_test.go
auth/
auth.go
auth_test.go
internal/
... internal packages
cmd/
prog1/
main.go
prog2/
main.gointernal目錄中實現服務器邏輯的GO軟件包。此外,由於該項目可能具有許多其他具有非GO文件的目錄,因此最好將所有GO命令放在CMD目錄中是一個好主意: project-root-directory/
go.mod
internal/
auth/
...
metrics/
...
model/
...
cmd/
api-server/
main.go
metrics-analyzer/
main.go
...
... the project ' s other directories with non-Go codeGO模型數據輸入和輸出是從源到目標流動的流。數據源(例如文件,網絡連接,甚至某些內存對象)可以建模為可以從中讀取或寫入數據的字節流。
fmt軟件包最常見的用法是對標準輸出和從標準輸入進行讀取。
type metalloid struct {
name string
number int32
weight float64
}
func main () {
var metalloids = [] metalloid {
{ "Boron" , 5 , 10.81 },
...
{ "Polonium" , 84 , 209.0 },
}
file , _ := os . Create ( "./metalloids.txt" )
defer file . Close ()
for _ , m := range metalloids {
fmt . Fprintf (
file ,
"%-10s %-10d %-10.3f n " ,
m . name , m . number , m . weight ,
)
}
} bufio軟件包提供了幾個功能,可以使用`io.writer界面對IO流進行緩衝的撰寫。
在bytes包中提供了共同的原始圖,以在存儲在內存中的字節塊上實現流io,以字節為bytes.Buffer表示。由於bytes.Buffer類型同時實現了io.Reader和io.Writer界面,因此使用流IO PINILITIVES將數據傳輸到存儲器中或從內存中流式傳輸是一個不錯的選擇。
package main
import (
"encoding/json"
"fmt"
)
type Measurement struct {
Height int
Weight int
}
type Person struct {
Name string
Age int
Measurement Measurement // Nested object
}
func main () {
bob := & Person {
Name : "Bob" ,
Age : 20 ,
}
bobRaw , _ := json . Marshal ( bob )
fmt . Println ( string ( bobRaw ))
// Raw data without Measurement field
aliceRaw := [] byte ( `{"name": "Alice", "age": 23}` )
var alice Person
if err := json . Unmarshal ( aliceRaw , & alice ); err != nil {
panic ( err )
}
fmt . Printf ( "%+v n " , alice )
}
// {"Name":"Bob","Age":20,"Measurement":{"Height":190,"Weight":75}}
// {Name:Alice Age:23 Measurement:{Height:0 Weight:0}} package main
import (
"encoding/json"
"fmt"
)
type Measurement struct {
Height int `json:"height"`
Weight int `json:"weight"`
}
type Person struct {
Name string `json:"who"`
Age int `json:"how old"`
Measurement Measurement `json:"mm"`
}
func main () {
bob := & Person {
Name : "Bob" ,
Age : 20 ,
}
bobRaw , _ := json . Marshal ( bob )
fmt . Println ( string ( bobRaw ))
// Raw data without Measurement field
aliceRaw := [] byte ( `{"who": "Alice", "how old": 23, "mm": {"height": 150, "weight": 40}}` )
var alice Person
if err := json . Unmarshal ( aliceRaw , & alice ); err != nil {
panic ( err )
}
fmt . Printf ( "%+v" , alice )
}
// {"who":"Bob","how old":20,"mm":{"height":0,"weight":0}}
// {Name:Alice Age:23 Measurement:{Height:150 Weight:40}} package main
import (
"encoding/json"
"fmt"
)
func main () {
// Raw data without Measurement field
aliceRaw := [] byte ( `{"name": "Alice", "age": 23, "measurement": {"height": 150, "weight": 40}}` )
var alice map [ string ] interface {}
if err := json . Unmarshal ( aliceRaw , & alice ); err != nil {
panic ( err )
}
// the object stored in the "mesurement" key is also stored
// as a map[string]interface{} type, and its type is asserted
// the interface{} type
measurement := alice [ "measurement" ].( map [ string ] interface {})
fmt . Printf ( "%+v n " , alice )
fmt . Printf ( "%+v n " , measurement )
}
// map[age:23 measurement:map[height:150 weight:40] name:Alice]
// map[height:150 weight:40]omitempty財產。 package main
import (
"encoding/json"
"fmt"
)
type Measurement struct {
Height int `json:"height"`
Weight int `json:"weight"`
}
type Person struct {
Name string `json:"name"`
Age int `json:"age,omitempty"`
Measurement Measurement `json:"measurement"`
}
func main () {
bob := & Person {
Name : "Bob" ,
Measurement : Measurement {
Height : 190 ,
Weight : 75 ,
},
}
bobRaw , _ := json . Marshal ( bob )
fmt . Println ( string ( bobRaw ))
}
// Age field is ignored
// {"name":"Bob","measurement":{"height":190,"weight":75}}注意:提示中有很多有用的事情。您可能需要檢查一下。
去網絡示例
基本的HTTP服務器有一些關鍵作業需要處理:
package main
import (
"fmt"
"net/http"
)
func main () {
// Process dynamic request
http . HandleFunc ( "/" , func ( w http. ResponseWriter , r * http. Request ) {
fmt . Fprintf ( w , "Welcome to my website!" )
})
// Serving static assets
fs := http . FileServer ( http . Dir ( "static/" ))
http . Handle ( "/static/" , http . StripPrefix ( "/static/" , fs ))
// Accept connections
http . ListenAndServe ( ":80" , nil )
}一個簡單的記錄中間件。
// basic-middleware.go
package main
import (
"fmt"
"log"
"net/http"
)
func logging ( f http. HandlerFunc ) http. HandlerFunc {
return func ( w http. ResponseWriter , r * http. Request ) {
log . Println ( r . URL . Path )
f ( w , r )
}
}
func foo ( w http. ResponseWriter , r * http. Request ) {
fmt . Fprintln ( w , "foo" )
}
func bar ( w http. ResponseWriter , r * http. Request ) {
fmt . Fprintln ( w , "bar" )
}
func main () {
http . HandleFunc ( "/foo" , logging ( foo ))
http . HandleFunc ( "/bar" , logging ( bar ))
http . ListenAndServe ( ":8080" , nil )
}中間軟件本身簡單將http.HandleFunc作為其參數之一,將其包裝並返回新的http.HandlerFunc 。
定義一種新的類型Middleware ,最終使將多個中間用水鍊鍊鏈接在一起。
如何創建新的中間件,樣板代碼:
func newMiddleware () Middleware {
// Create a new Middleware
middleware := func ( next http. HandlerFunc ) http. HandlerFunc {
// Define the http.HandlerFunc which is called by the server eventually
handler := func ( w http. ResponseWriter , r * http. Request ) {
// ... do middleware things
// Call the next middleware/handler in chain
next ( w , r )
}
// Return newly created handler
return handler
}
// Return newly created middleware
return middleware
} // advanced-middleware.go
package main
import (
"fmt"
"log"
"net/http"
"time"
)
type Middleware func (http. HandlerFunc ) http. HandlerFunc
// Logging logs all requests with its path & the time it took to process
func Logging () Middleware {
// Create a new Middleware
return func ( f http. HandlerFunc ) http. HandlerFunc {
// Define the http.HandlerFunc
return func ( w http. ResponseWriter , r * http. Request ) {
// Do middleware things
start := time . Now ()
defer func () { log . Println ( r . URL . Path , time . Since ( start )) }()
// Call the next middleware/handler in chain
f ( w , r )
}
}
}
// Method ensures that url can only be requested with a specific method, else returns a 400 Bad Request
func Method ( m string ) Middleware {
// Create a new Middleware
return func ( f http. HandlerFunc ) http. HandlerFunc {
// Define the http.HandlerFunc
return func ( w http. ResponseWriter , r * http. Request ) {
// Do middleware things
if r . Method != m {
http . Error ( w , http . StatusText ( http . StatusBadRequest ), http . StatusBadRequest )
return
}
// Call the next middleware/handler in chain
f ( w , r )
}
}
}
// Chain applies middlewares to a http.HandlerFunc
func Chain ( f http. HandlerFunc , middlewares ... Middleware ) http. HandlerFunc {
for _ , m := range middlewares {
f = m ( f )
}
return f
}
func Hello ( w http. ResponseWriter , r * http. Request ) {
fmt . Fprintln ( w , "hello world" )
}
func main () {
http . HandleFunc ( "/" , Chain ( Hello , Method ( "GET" ), Logging ()))
http . ListenAndServe ( ":8080" , nil )
}本節主要摘自:https://github.com/zalopay-oss/go-advanced/blob/master/ch3-rpc/ch3-01-rpc-go.md
// 13/rpc/rpcserver/main.go
package main
import (
"log"
"net"
"net/rpc"
)
type HelloService struct {}
// Only methods that satisfy these criteria will be made available for remote access; other methods will be ignored:
// - the method's type is exported.
// - the method is exported.
// - the method has two arguments, both exported (or builtin) types.
// - the method's second argument is a pointer.
// - the method has return type error.
// func (t *T) MethodName(argType T1, replyType *T2) error
func ( p * HelloService ) Hello ( request string , reply * string ) error {
* reply = "Hello " + request
return nil
}
func main () {
rpc . RegisterName ( "HelloService" , new ( HelloService ))
listener , err := net . Listen ( "tcp" , ":8081" )
if err != nil {
log . Fatal ( "Listen TCP error:" , err )
}
log . Println ( "Server is ready" )
for {
// accept connection
conn , err := listener . Accept ()
if err != nil {
log . Fatal ( "Accept error:" , err )
}
// serve client in another goroutine
go func () {
log . Println ( "Accept new client:" , conn . RemoteAddr ())
rpc . ServeConn ( conn )
}()
}
} // 13/rpc/rpcclient/main.go
package main
import (
"log"
"net/rpc"
)
func main () {
client , err := rpc . Dial ( "tcp" , "localhost:8081" )
if err != nil {
log . Fatal ( "Dialing error:" , err )
}
var reply string
if err = client . Call ( "HelloService.Hello" , "Kien" , & reply ); err != nil {
log . Fatal ( err )
}
log . Println ( reply )
} # Server
$ go run examples/13/rpc/rpcserver/main.go
2023/08/09 16:29:29 Server is ready
2023/08/09 16:29:30 Accept new client: 127.0.0.1:38728
2023/08/09 16:29:31 Accept new client: 127.0.0.1:38734
# Client
$ go run examples/13/rpc/rpcclient/main.go
2023/08/09 16:29:30 Hello Kien
$ go run examples/13/rpc/rpcclient/main.go
2023/08/09 16:29:31 Hello Kienprotoc : # Ubuntu
# https://grpc.io/docs/protoc-installation/#install-using-a-package-manager
$ sudo apt install -y protobuf-compiler
# install go plugin
$ go install github.com/golang/protobuf/protoc-gen-go@latesthello.proto : // version proto3
syntax = "proto3" ;
// generated package name
package main ;
message String {
string value = 1 ;
}生成Golang源代碼:
GRPC是可以在任何地方運行的高性能,開源遠程過程調用(RPC)框架。它使客戶端和服務器應用程序能夠透明地通信,並使構建連接的系統更容易。
GRPC服務器實現了服務接口,並運行RPC服務器來處理其服務方法的客戶端呼叫。在客戶端,客戶端具有與服務器相同的方法的存根(僅以某些語言的客戶端稱為客戶端)。
本節是關於添加新軟件包的。
unique包裝來源:https://go.dev/blog/unique
var internPool map [ string ] string
// Intern returns a string that is equal to s but that may share storage with
// a string previously passed to Intern.
func Intern ( s string ) string {
pooled , ok := internPool [ s ]
if ! ok {
// Clone the string in case it's part of some much bigger string.
// This should be rare, if interning is being used well.
pooled = strings . Clone ( s )
internPool [ pooled ] = pooled
}
return pooled
}unique軟件包引入了類似於Intern的功能。但這也與Intern不同:Handle[T]具有兩個Handle[T]值相等的屬性,並且僅當用於創建它們的值相等時。兩個Handle[T]值的比較便宜:取決於指針比較。net/netip軟件包,該庫中的net/netip軟件包無所不包,該庫是netip.addr結構的一部分addrDetail類型的值。netip.Addr的平均內存足跡,而它們是規範化的平均netip.Addr值的事實更有效地比較,因為比較區域名稱是一個簡單的指針比較。 // Addr represents an IPv4 or IPv6 address (with or without a scoped
// addressing zone), similar to net.IP or net.IPAddr.
type Addr struct {
// Other irrelevant unexported fields...
// Details about the address, wrapped up together and canonicalized.
z unique. Handle [ addrDetail ]
}
// addrDetail indicates whether the address is IPv4 or IPv6, and if IPv6,
// specifies the zone name for the address.
type addrDetail struct {
isV6 bool // IPv4 is false, IPv6 is true.
zoneV6 string // May be != "" if IsV6 is true.
}
var z6noz = unique . Make ( addrDetail { isV6 : true })
// WithZone returns an IP that's the same as ip but with the provided
// zone. If zone is empty, the zone is removed. If ip is an IPv4
// address, WithZone is a no-op and returns ip unchanged.
func ( ip Addr ) WithZone ( zone string ) Addr {
if ! ip . Is6 () {
return ip
}
if zone == "" {
ip . z = z6noz
return ip
}
ip . z = unique . Make ( addrDetail { isV6 : true , zoneV6 : zone })
return ip
}頁面列出了一些有興趣了解golang的程序員的資源。
糟糕,實際上,您可以參考完美的列表。