Go 語言設計哲學之十三:理解包導入路徑的含義

2021-02-22 面向加薪學習

Go 語言是使用包(package)作為基本單元來組織源碼的, Go 程序就是這些包連結起來而構建的。與C 語言的頭文件包含機制相比則是「先進」了許多。

即便是每次編譯都是從頭開始。避免了 C 語言那種通過頭文件分析依賴的巨大開銷。Go 語言以包為基本構建單元的構建模型,依賴分析非常簡單。

一 構建過程

Go 編譯速度快從三個方面分析:

1.Go 要求每個源文件在開頭處顯式地import所有依賴的包,Go編譯器不必讀取和處理整個文件就可以確定其依賴的包列表; 

2.Go 要求包之間不能存在循環依賴。由於無環,包可以被單獨編譯,也可以並行編譯; 

3.已編譯的 Go 包對應的目標文件(xxx.o 或 xxx.a)中,如下 

    1) 該包本身的導出符號信息。

    2) 還記錄了其所依賴包的導出符號信息。

這樣,Go編譯器在編譯某包M時,針對M依賴的每個包導入(比如:導入包N),只需讀取一個目標文件即可(比如:N包編譯成的目標文件,該目標文件中已經包含了N包的依賴包的導出信息),而無需再讀取其他文件中的信息了。

通過 package 關鍵字聲明 Go 源文件所屬的包: 

上述源碼表示:文件 xx.go 是包 x 的一部分。

使用 import 關鍵字導入依賴的標準庫包或第三方包: 

import (     "fmt"     // 標準庫包導入     「x/y/z"  // 第三方包導入 ) 

func main() { z.FuncName() fmt.Println("Go!Go!Go!") }

看到上面代碼都會想到將import後面的」z」、「fmt」與z.FuncName()和fmt.Println()中的z和fmt認同一個語法元素:包名。 但是以後深入學習Go語言後,發現並非這樣。比如實施分布式消息框架nsq提供的官方client包時,包導入如下: 

import 「github.com/nsqio/go-nsq」

但是使用導出函數的時候,我們不是go-nsq.FuncName(),而是nsq.FuncName: 

consumer,_:=nsq.NewConsumer("write_order", "ch", config) 

 

你可能會問最後一個分段到底代表什麼?是包名稱?是一個路徑? 

Go程序構建過程和其他主流靜態編譯語言一樣,Go語言的程序構建簡單說是由編譯(compile)和連結(link)兩個階段。 

一個非main包在編譯後會對應生成一個.a文件,該文件可以理解為是Go包的目標文件(是通過 pack 工具($GOROOT/pkg/tool/darwin_amd64/pack)對.o文件打包後形成的.a).默認情況下在編譯過程中.a文件生成臨時目錄下,除非使用go install安裝到$GOPATH/pkg下(Go1.11版本前),否則你看不到.a文件。如果構建可執行程序,那麼.a文件會在構建可執行程序的連結階段起使用。

標準庫包的源碼文件在$GOROOT/src 下面,而對應的 .a 文件存放在$GOROOT/pkg/darwin_amd64 下(以 MacOS 上為例;如果是 linux,則是 linux_amd64)

那麼構建Go程序時,編譯器會重新編譯依賴包的源文件還是直接連結包的.a文件呢?

在使用第三方包的時候,當第三方包原始碼存在且對應的.a已安裝的情況下,編譯器連結的仍是根據第三方包最新原始碼編譯出來的.a文件,而不是之前已經安裝到

$GOPATH/pkg/darwin_amd64 下面的目標文件。

那Go 標準庫中的包也是這樣的嗎?

默認情況下對於標準庫中的包,編譯器直接連結的是$GOROOT/pkg/darwin_amd64下的.a文件。

二 路徑名?包名?

通過上面的知識,知道了編譯器在編譯過程中必然要使用的是編譯單元(包)所依賴的包的源碼。而編譯器要找到依賴包的源碼文件就需要知道依賴包的源碼路徑。這個路徑由兩部分組成:

    1.基礎搜索路徑

    2.包導入路徑

基礎搜索包是一個全局的設置,下面介紹一下:

所有包(標準庫還是第三方包)的源碼基礎搜索路徑都包括 $GOROOT/src

在上述基礎搜索路徑的基礎上,不同版本的基礎搜索路徑有不同:

1.11 之前

       $GOPATH/src

1.11-1.12 有三種:

      經典gopath模式,GO111MODULE=off , $GOPATH/src

      module-aware 模式,GO111MODULE=on, $GOPATH/pkg/mod

      auto 模式,GO111MODULE=auto,在$GOPATH/src 路徑下,與 gopath 模式相同;在$GOPATH/src 路徑外且包含 go.mod,與 module-aware 模式相同。

1.13 有兩種

    經典gopath 模式,GO111MODULE=off,$GOPATH/src

    module-aware 模式下,GO111MODULE=on/auto,$GOPATH/pkg/mod; 

1.13後 

    只有 module-aware 模式,即只在 module 緩存的目錄下搜索包的源碼。 

搜索路徑第二部分,是位於每個包源碼文件頭部的包導入路徑。基礎搜索路徑與包導入路徑結合在一起,Go編譯器就可以確定一個包的所有依賴包的源碼路徑的集合,這樣集合構成了Go編譯器的源碼搜索路徑空間。 

三 同一源碼的依賴包在同一源碼搜索路徑下包名衝突,怎麼辦? 

package main 
import ( "github.com/xxx/p1/pkg/pkg11" 「github.com/xxx/p2/pkg/pkg11" )
func main() { pkg1.Func1() }

當你有這樣的import的時候,運行main,直接報錯。會告訴你包衝突。如何解決呢?

package main
import ( p1 "github.com/xxx/p1/pkg/pkg11" p2 「github.com/xxx/p2/pkg/pkg11")
func main() { p1.Func1() p2.Func1()}

這樣就ok了,解決了問題。

相關焦點

  • 手把手教會你帶你理解Go語言中的包
    所以今天就來學一下包這個東西,將咱們的代碼拆分一下。包包可以理解為存放多個.go的文件夾但是這個文件夾下面的第一行的package後面跟的不再是main了而是文件名,就像這樣。目錄那我們如何在main.go中使用上述建立的包調用裡面的方法呢?這就是要導入它們了。
  • Go語言的設計哲學之二: 組合
    針對這種情況,很多人會問:那 Go 語言是如何將程序的各個部分有機地耦合在一起的呢?Go 語言遵從的設計哲學也是組合。在介紹組合之前,我們可以先來了解一下 Go 在語法元素設計時是如何為組合哲學的應用打下堅實基礎的。
  • 「語言實踐」Go語言文檔自動化之go-swagger
    1 go-swagger依賴包獲取go-swagger中在github的倉庫下的依賴包如下,主要包含可以對語法進行校驗的govalidator,文檔化的標準specification的go-openapi,還有網絡處理的golang.org官網旗下的net和text。
  • Go語言:1分鐘寫下第一個Go程序,並在終端裡以指令方式運行
    設置環境變量$GOPATH、$PATH從官網(https://golang.org/dl/,國內可以從https://studygolang.com/dl下載)下載了Go語言安裝包,並安裝以後,此時在終端裡執行指令:go version一般都可以顯示版本號了。
  • [GO語言基礎] 一.為什麼我要學習Golang以及GO語言入門普及
    學習體驗也十分順暢,不得不說,Go 是一種經過精心設計的實用性語言。舉個例子:一旦你知悉了 Go 的語法,就能將其他語言中慣用法延續到 Go 中。只要你學會一些 Go,就可以相對輕易地推測 Go 語言的其他特性。憑藉一些來自其他語言的知識,我能夠閱讀並理解 Go 代碼,而不需要過多的搜索(Google)。與 C/C++、Java、Python 等相比,Go 並沒有那麼多痛點,而且更具生產力。
  • GO語言入門(第一個go程序)
    本文節選自《go入門指南》GO語言介紹指導設計原則
  • Hello Go!
    Go 的語法特性相比其他語言來說更加簡潔,只有25個關鍵詞靜態編譯型語言,部署非常方便,只需要 build 一個二進位文件扔到伺服器上國內網際網路公司 golang 崗位日益增多,是高性能web後端、雲服務、區塊鏈等領域一門比較有前景的語言,有docker/k8s這種殺手級應用    也有一些一直被人噴的地方(當然設計哲學這種問題,不同人有不同看法):
  • 大話go 語言:談談 go 語言的類型系統
    引言:最近地鐵上沒事,準備花一點時間研究下 go 語言,分兩節來分享,這節首選『類型系統』因為我覺得類型系統是一門語言之核心。五年前,曾被面試官面試到 C 和 C++的區別。其中有被問到如何用 C 語言實現面向對象編程。
  • 解讀Go語言的2020:變革前夜
    作者認為,正因為 Go 語言有著崇尚簡約和實用主義的編程哲學,廣大軟體工程師才會如此地愛用它。 更重要的是,Go 軟體工程師的薪資待遇也是相當不錯的。 關於 Go 語言的泛型,大家可以參看 Ian Lance Taylor 和 Robert Griesemer 在 2020 年 11 月 25 日發布的最新設計草案。而對於新的錯誤處理機制,大家可以去看 Go 2 設計草案中「Error handling」部分。作者就不在這裡多說了。
  • Go2:實驗,簡化,出貨! - OSCHINA - 中文開源技術交流社區
    去年我們又開始進行探索和實驗,去年夏天,我們在Gophercon的基礎上提出了一個基於合約理念的新設計。 我們一直在進行實驗和簡化,我們一直在與程式語言理論專家合作,以更好地理解設計。總的來說,我希望我們朝著一個良好的方向前進,朝著一個簡化Go開發的設計。 即便如此,我們可能會發現這種設計也不起作用。 我們可能不得不放棄這個實驗,並根據我們學到的東西調整我們的路徑。
  • R語言數據導入與導出
    R語言數據導入與導出整這麼一個系列,還是因為學R語言時遇到過一個非常「小白友好」的網站「DataScience Made Simple」。相信很多人搜到過這個網站,或許你在意到或許並沒在意。年前試著和作者發了一封郵件,想要把他這個網站做成漢語版的帖子發在公眾號上,讓我感動的是作者團隊欣然同意。於是就想著搞這麼一個系列,能不能堅持下來還不好說……且行且珍惜吧。
  • 數據科學養成記 之 R語言基礎(1)——數據導入
    在配置好R語言的環境,安裝好RStudio後,想要進行數據分析,我們首先要進行數據的導入。R語言支持的數據類型很多,包括資料庫文件,excel文件,csv文件,txt文件等等。本文介紹在作者日常中最常用到的csv及txt文件的讀取,excel文件雖然也是日常中的常用文件,但是由於excel本身的行數限制及讀取需要專用包的支持,在我的日常中經常是將excel另存為csv或者txt再導入R中進行數據分析。
  • Go語言(Golang)環境搭建詳解
    下載要搭建Go語言開發環境,我們第一步要下載go的開發工具包,目前最新穩定版本是v1.9,Go1.9增加了一些新特性,我這裡有一篇講 Go語言 | Go 1.9 新特性 Type Alias詳解 的,大家可以參考。Go為我們所熟知的所有平臺架構提供了開發工具包,比如我們熟知的Linux、Mac和Windows,其他的還有FreeBSD等。
  • 從此再無包下載的任何煩惱:看高手是如何參透 Go Module 的?
    是啟用了 Go moduels 的項目所必須的最重要的文件,它描述了當前項目(也就是當前模塊)的元信息,每一行都以一個動詞開頭,目前有以下 5 個動詞:這裡的填寫格式基本為包引用路徑+版本號,另外比較特殊的是 go $version,目前從 Go1.13 的代碼裡來看,還只是個標識作用,暫時未知未來是否有更大的作用。
  • 入門教程:花 5 分鐘學習 Go 語言
    = nil {    return fmt.Errorf("error writing text to fiel: %w", err)  }  return nil}創建和導入包Go 支持 module(包管理),允許你創建並導入相關包,在剛開始的基本章節中,我們已經知道如何在啟動新項目時使用 go mod init 創建模塊。
  • Go 在 Google:服務於軟體工程的語言設計(翻譯)(一)
    最近在寫 Go 語言實戰系列的文章,中間想聊一下 Go 的設計原則,發現自己理解得還是不夠深入,寫得辭不達意。然後找到了 Rob Pike 在 8 年前的演講稿,拜讀學習之後,想推薦給我的讀者作為學習資料。結果在中文網際網路只找到了 OSCHINA 上 13 年的眾包翻譯,再也沒找到其他翻譯版本。
  • 深度解密Go語言之unsafe
    然而,相比於 C 語言中指針的靈活,Go 的指針多了一些限制。但這也算是 Go 的成功之處:既可以享受指針帶來的便利,又避免了指針的危險性。限制一:Go的指針不能進行數學運算。但是高階的 Gopher,怎麼能不會使用 unsafe 包呢?它可以繞過 Go 語言的類型系統,直接操作內存。例如,一般我們不能操作一個結構體的未導出成員,但是通過 unsafe 包就能做到。unsafe 包讓我可以直接讀寫內存,還管你什麼導出還是未導出。
  • 什麼是語言哲學?這5個問題,語言哲學很關心
    什麼是語言哲學?1分鐘了解下。語言哲學的內涵語言哲學即是對語言的哲學思考的一門學問,包括語言實在論,語言本體論,語言意義等。語言哲學更關注語言意義的影響,而不是對意義的詳細描述。語言哲學關心五個基本問題。
  • 《Go語言實戰》筆記(二) | Go開發工具
    可以發現,go支持的子命令很多,同時還支持查看一些【主題】。我們可以使用go help [command]或者go help [topic]查看一些命令的使用幫助,或者關於某個主題的信息。大部分go的命令,都是接受一個全路徑的包名作為參數,比如我們經常用的go build。
  • 教師常用教法集錦:導入、提問、啟發、語言表達、結尾、板書設計
    導語設計的一般方法如下: 1. 懸念導入法 教師精心設計一個緊扣學生心弦的情境或問題,造成學生渴望的心理狀態,使學生欲罷不能,頓生一種強烈的求知慾,然後自然引出所講內容。 2.