iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 8
0
Software Development

Go Distributed & Go Consistently系列 第 8

Day8 Basic Go (Initialize, Scope)

今天我們要回來重新檢視一下 golang 的各種 Scope(作用域),在開始 scope 的說明前還必須提到 golang 程式在 runtime 初始化的順序,確保我們對運行時每一個階段的掌握。

Initialization Order

當我們開始執行 golang 的程序時,首先會將 import packages 內部的常數變數與 init() 完成後,才開始當前 main package 下的常數變數與 init(),最後執行 main()。此外需注意的是每個 package 內的 init() 都只會被執行一次,如同其保留的命名為“初始化”時使用。

  1. import package
  2. const & var
  3. init()
  4. main()

請下載執行 basicGo/init/main.go 觀察其執行順序,觀察結果應如下。

> go run basicGo/init/main.go

package init() started
packageConst packageConst
packageVar packageVar
main init() started
mainConst mainConst
mainVar mainVar
main func started
hello
hello2

Scope

Scope(作用域) 的了解除了最基本的程式是否能正常運行外,也關乎你的變數是否容易被污染,若能仔細控制每一個變數的作用範圍的話,將能將有效提升服務的可靠度。

Public & Private

我們直接使用範例程式碼說明,如果是使用 VScode 當作編輯器的話可以看到很清楚的提示。當我們的主程式 import scpackage 使用的時候,能使用的方法只有大寫的 scpackage.Create(),能使用的 struct 只有 scpackage.Clinet。

Golang 編譯上會將

  • 大寫視為Public
  • 小寫視為Private

scpackage/example.go

package scpackage

//Clinet example of scope
type Clinet struct {
	IP      string
	Host    string
	setting detail
}

//小寫private,無法被其他 package 直接存取
type detail struct {
	maxClient int
	maxIdle   int
}

//Create example of scope
func Create(ip, host string) *Clinet {
	return &Clinet{
		IP:      ip,
		Host:    host,
		setting: newSetting(),
	}
}

//小寫private,無法被其他 package 直接存取
func newSetting() detail {
	return detail{
		maxClient: 100,
		maxIdle:   10,
	}
}

可直接使用的只有大寫Public
public

得到的 struct 內的 field 也是僅能直接操作 大寫Public 的部分
public struct

Block

關於 block scope (區域作用域),我們可以簡單地用 "{ }" 範圍來觀察變數作用域影響,但實務上我們避免在 block 內以相同名稱命名變數。範例中 if{ } 內重新 var new 同樣名稱的變數,僅為方便讓我們了解 scope 的效果。

scope/main.go

package main

import (
	"fmt"
	"ithome12/basicGo/scpackage"
)

var global = "a global var"

func main() {
	//scope of package
	client := scpackage.Create("127.0.0.1", "localhost")
	fmt.Printf("%+v\n", client)

	//scope of block
	myFuncA()
	myFuncB()
	fmt.Println(global)

	new := "new"
	for i := 0; i < 1; i++ {
		//new 被宣告於迴圈{}外,符合作用域可使用
		//i 屬於此迴圈內宣告變數,故僅能作用於迴圈內
		//newInFor 同 i,屬於此迴圈內宣告變數,故僅能作用於迴圈內
		newInFor := "hi"
		fmt.Println(i, new, newInFor)
	}

	//每個block{}都分別為新的作用域
	//if 判斷式內的 "new" 變數與 先前的 "new" 變數,為不同實體,並有著不同記憶體位址
	if len(new) > 0 {
		new := "xxx"
		fmt.Println(new, &new)
	}
	fmt.Println(new, &new)
}

func myFuncA() {
	fmt.Println(global)
	global = "changed by myFuncA"
}

func myFuncB() {
	fmt.Println(global)
	global = "changed by myFuncB"
}

result output

&{IP:127.0.0.1 Host:localhost setting:{maxClient:100 maxIdle:10}}
a global var
changed by myFuncA
changed by myFuncB
0 new hi
xxx 0xc000010280
new 0xc000010250

上一篇
Day7 Basic Go (Run, Syntax, Struct)
下一篇
Day9 Goroutine & Concurrency
系列文
Go Distributed & Go Consistently30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言