首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

Golang语法、技巧和窍门

Golang简介

• 命令式语言

• 静态类型

• 语法标记类似于C(但括号较少且没有分号),结构类似Oberon-2

• 编译为本机代码(没有JVM)

• 没有类,但有带有方法的结构

• 接口

• 没有实现继承。不过有type嵌入。

• 函数是一等公民

• 函数可以返回多个值

• 支持闭包

• 指针,但没有指针算术

• 内置并发原语:Goroutines和Channels

基本语法

你好,世界

文件hello.go:

package main

import "fmt"

func main() {

fmt.Println("Hello Go")

}

$ go run hello.go运算符算术运算符

比较运算符

逻辑运算符

其他

声明

类型在标识符之后!

var foo int // declaration without initialization

var foo int = 42 // declaration with initialization

var foo, bar int = 42, 1302 // declare and init multiple vars at once

var foo = 42 // type omitted, will be inferred

foo := 42 // shorthand, only in func bodies, omit var keyword, type is always implicit

const constant = "This is a constant"

// iota can be used for incrementing numbers, starting from 0

const (

_ = iota

a

b

c = 1 

d

)

fmt.Println(a, b) // 1 2 (0 is skipped)

fmt.Println(c, d) // 8 16 (2^3, 2^4)函数

// a simple function

func functionName() {}

// function with parameters (again, types go after identifiers)

func functionName(param1 string, param2 int) {}

// multiple parameters of the same type

func functionName(param1, param2 int) {}

// return type declaration

func functionName() int {

return 42

}

// Can return multiple values at once

func returnMulti() (int, string) {

return 42, "foobar"

}

var x, str = returnMulti()

// Return multiple named results simply by return

func returnMulti2() (n int, s string) {

n = 42

s = "foobar"

// n and s will be returned

return

}

var x, str = returnMulti2()函数作为值和闭包

func main() {

// assign a function to a name

add := func(a, b int) int {

return a + b

}

// use the name to call the function

fmt.Println(add(3, 4))

}

// Closures, lexically scoped: Functions can access values that were

// in scope when defining the function

func scope() func() int{

outer_var := 2

foo := func() int { return outer_var}

return foo

}

func another_scope() func() int{

// won't compile because outer_var and foo not defined in this scope

outer_var = 444

return foo

}

// Closures

func outer() (func() int, int) {

outer_var := 2

inner := func() int {

outer_var += 99 // outer_var from outer scope is mutated.

return outer_var

}

inner()

return inner, outer_var // return inner func and mutated outer_var 101

}可变参数函数

func main() {

fmt.Println(adder(1, 2, 3))  // 6

fmt.Println(adder(9, 9)) // 18

nums := []int{10, 20, 30}

fmt.Println(adder(nums...)) // 60

}

// By using ... before the type name of the last parameter you can indicate that it takes zero or more of those parameters.

// The function is invoked like any other function except we can pass as many arguments as we want.

func adder(args ...int) int {

total := 0

for _, v := range args { // Iterates over the arguments whatever the number.

total += v

}

return total

}内置类型

bool

string

int  int8  int16  int32  int64

uint uint8 uint16 uint32 uint64 uintptr

byte // alias for uint8

rune // alias for int32 ~= a character (Unicode code point) - very Viking

float32 float64

complex64 complex128

所有Go的预声明标识符都定义在builtin包中。

类型转换

var i int = 42

var f float64 = float64(i)

var u uint = uint(f)

// alternative syntax

i := 42

f := float64(i)

u := uint(f)包

• 在每个源文件的顶部声明包

• 可执行文件位于main包中

• 约定:包的名称等于导入路径的最后一个部分(导入路径math/rand=> 包rand)

• 大写标识符:导出的(可以从其他包中访问)

• 小写标识符:私有的(不能从其他包中访问)

控制结构

判断

func main() {

// Basic one

if x > 10 {

return x

} else if x == 10 {

return 10

} else {

return -x

}

// You can put one statement before the condition

if a := b + c; a 

return a

} else {

return a - 42

}

// Type assertion inside if

var val interface{} = "foo"

if str, ok := val.(string); ok {

fmt.Println(str)

}

}循环

// There's only `for`, no `while`, no `until`

for i := 1; i 

}

for ; i 

}

for i 

}

for { // you can omit the condition ~ while (true)

}

// use break/continue on current loop

// use break/continue with label on outer loop

here:

for i := 0; i 

for j := i + 1; j 

if i == 0 {

continue here

}

fmt.Println(j)

if j == 2 {

break

}

}

}

there:

for i := 0; i 

for j := i + 1; j 

if j == 1 {

continue

}

fmt.Println(j)

if j == 2 {

break there

}

}

}条件

// switch statement

switch operatingSystem {

case "darwin":

fmt.Println("Mac OS Hipster")

// cases break automatically, no fallthrough by default

case "linux":

fmt.Println("Linux Geek")

default:

// Windows, BSD, ...

fmt.Println("Other")

}

// as with for and if, you can have an assignment statement before the switch value

switch os := runtime.GOOS; os {

case "darwin": ...

}

// you can also make comparisons in switch cases

number := 42

switch {

case number 

fmt.Println("Smaller")

case number == 42:

fmt.Println("Equal")

case number > 42:

fmt.Println("Greater")

}

// cases can be presented in comma-separated lists

var char byte = '?'

switch char {

case ' ', '?', '&', '=', '#', '+', '%':

fmt.Println("Should escape")

}数组, 切片, 遍历数组

var a [10]int // declare an int array with length 10. Array length is part of the type!

a[3] = 42     // set elements

i := a[3]     // read elements

// declare and initialize

var a = [2]int{1, 2}

a := [2]int{1, 2} //shorthand

a := [...]int{1, 2} // elipsis -> Compiler figures out array length切片

var a []int                              // declare a slice - similar to an array, but length is unspecified

var a = []int {1, 2, 3, 4}               // declare and initialize a slice (backed by the array given implicitly)

a := []int{1, 2, 3, 4}                   // shorthand

chars := []string{0:"a", 2:"c", 1: "b"}  // ["a", "b", "c"]

var b = a[lo:hi] // creates a slice (view of the array) from index lo to hi-1

var b = a[1:4]  // slice from index 1 to 3

var b = a[:3]  // missing low index implies 0

var b = a[3:]  // missing high index implies len(a)

a =  append(a,17,3) // append items to slice a

c := append(a,b...) // concatenate slices a and b

// create a slice with make

a = make([]byte, 5, 5) // first arg length, second capacity

a = make([]byte, 5) // capacity is optional

// create a slice from an array

x := [3]string{"Лайка", "Белка", "Стрелка"}

s := x[:] // a slice referencing the storage of x数组和切片的操作

len(a)返回数组/切片的长度。这是一个内置函数,而不是数组的属性/方法。

// loop over an array/a slice

for i, e := range a {

// i is the index, e the element

}

// if you only need e:

for _, e := range a {

// e is the element

}

// ...and if you only need the index

for i := range a {

}

// In Go pre-1.4, you'll get a compiler error if you're not using i and e.

// Go 1.4 introduced a variable-free form, so that you can do this

for range time.Tick(time.Second) {

// do it once a sec

}哈希表

m := make(map[string]int)

m["key"] = 42

fmt.Println(m["key"])

delete(m, "key")

elem, ok := m["key"] // test if key "key" is present and retrieve it, if so

// map literal

var m = map[string]Vertex{

"Bell Labs": {40.68433, -74.39967},

"Google":    {37.42202, -122.08408},

}

// iterate over map content

for key, value := range m {

}结构体

Go中没有类,只有结构体。结构体可以拥有方法。

// 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]interface{}更经济和更安全。

point := struct {

X, Y int

}{1, 2}指针

p := Vertex{1, 2}  // p is a Vertex

q := &p            // q is a pointer to a Vertex

r := &Vertex{1, 2} // r is also a pointer to a Vertex

// The type of a pointer to a Vertex is *Vertex

var s *Vertex = new(Vertex) // new creates a pointer to a new struct instance接口

// interface declaration

type Awesomizer interface {

Awesomize() string

}

// types do *not* declare to implement interfaces

type Foo struct {}

// instead, types implicitly satisfy an interface if they implement all required methods

func (foo Foo) Awesomize() string {

return "Awesome!"

}嵌入

Go中没有子类化。相反,有接口和结构体嵌入。

// 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错误

Go中没有异常处理。相反,可能产生错误的函数只是声明了一个额外的返回值,类型为error。这是error接口:

// The error built-in interface type is the conventional interface for representing an error condition,

// with the nil value representing no error.

type error interface {

Error() string

}

这是一个示例:

func sqrt(x float64) (float64, error) {

if x 

return 0, errors.New("negative value")

}

return math.Sqrt(x), nil

}

func main() {

val, err := sqrt(-1)

if err != nil {

// handle error

fmt.Println(err) // negative value

return

}

// All is good, use `val`.

fmt.Println(val)

}并发协程

Goroutines是轻量级线程(由Go管理,而不是操作系统线程)。go f(a, b)启动一个新的goroutine来运行f(假设f是一个函数)。

// just a function (which can be later started as a goroutine)

func doStuff(s string) {

}

func main() {

// using a named function in a goroutine

go doStuff("foobar")

// using an anonymous inner function in a goroutine

go func (x int) {

// function body goes here

}(42)

}通道

ch := make(chan int) // create a channel of type int

ch 

v := 

// Non-buffered channels block. Read blocks when no value is available, write blocks until there is a read.

// Create a buffered channel. Writing to a buffered channels does not block if less than  unread values have been written.

ch := make(chan int, 100)

close(ch) // closes the channel (only sender should close)

// read from channel and test if it has been closed

v, ok := 

// if ok is false, channel has been closed

// Read from channel until it is closed

for i := range ch {

fmt.Println(i)

}

// select blocks on multiple channel operations, if one unblocks, the corresponding case is executed

func doStuff(channelOut, channelIn chan int) {

select {

case channelOut 

fmt.Println("We could write to channelOut!")

case x := 

fmt.Println("We could read from channelIn")

case 

fmt.Println("timeout")

}

}通道原理

• 向空通道发送会永远阻塞

var c chan string

// fatal error: all goroutines are asleep - deadlock!

• 从空通道接收会永远阻塞。

var c chan string

fmt.Println(

// fatal error: all goroutines are asleep - deadlock!

• 向已关闭的通道发送会引发恐慌。

var c = make(chan string, 1)

close(c)

// panic: send on closed channel

• 从已关闭的通道接收会立即返回零值。

var c = make(chan int, 2)

close(c)

for i := 0; i 

fmt.Printf("%d ", 

}

// 1 2 0

打印

fmt.Println("Hello, 你好, नमस्ते, Привет, ᎣᏏᏲ") // basic print, plus newline

p := struct { X, Y int }{ 17, 2 }

fmt.Println( "My point:", p, "x coord=", p.X ) // print structs, ints, etc

s := fmt.Sprintln( "My point:", p, "x coord=", p.X ) // print to string variable

fmt.Printf("%d hex:%x bin:%b fp:%f sci:%e",17,17,17,17.0,17.0) // c-ish format

s2 := fmt.Sprintf( "%d %f", 17, 17.0 ) // formatted print to string variable

hellomsg := `

"Hello" in Chinese is 你好 ('Ni Hao')

"Hello" in Hindi is नमस्ते ('Namaste')

` // multi-line string literal, using back-tick at beginning and end反射类型切换

类型切换类似于常规的switch语句,但类型切换中的情况指定要与给定接口值持有的值的类型进行比较的类型,而不是值。

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)

}片段文件嵌入

Go程序可以使用"embed"包嵌入静态文件,如下所示:

package main

import (

"embed"

"log"

"net/http"

)

// content holds the static content (2 files) for the web server.

//go:embed a.txt b.txt

var content embed.FS

func main() {

http.Handle("/", http.FileServer(http.FS(content)))

log.Fatal(http.ListenAndServe(":8080", nil))

}

完整的Playground示例

HTTP服务器

package main

import (

"fmt"

"net/http"

)

// define a type for the response

type Hello struct{}

// let that type implement the ServeHTTP method (defined in interface http.Handler)

func (h Hello) ServeHTTP(w http.ResponseWriter, r *http.Request) {

fmt.Fprint(w, "Hello!")

}

func main() {

var h Hello

http.ListenAndServe("localhost:4000", h)

}

// Here's the method signature of http.ServeHTTP:

// type Handler interface {

//     ServeHTTP(w http.ResponseWriter, r *http.Request)

// }

  • 发表于:
  • 原文链接https://page.om.qq.com/page/OLTzEfr0uyTRoIS148_6eutg0
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券