欧美vvv,亚洲第一成人在线,亚洲成人欧美日韩在线观看,日本猛少妇猛色XXXXX猛叫

新聞資訊

    Go 是 2007 年末由谷歌創立的一種程序設計語言,2009 年 11 月以開源形式發行。自那以后,Go 就作為一個公共項目運作,有成千上萬的個人和幾十家公司作出貢獻。Go 已經成為一種很受歡迎的語言,用于構建云計算基礎設施:Linux 容器管理器 Docker 和容器部署系統 Kubernetes 是由 Go 開發的一種核心云計算技術。現如今,Go 已經成為了各大云計算提供商的重要基礎設施的基礎,也是云原生計算基金會托管的大多數項目的實現語言。


    有許多理由讓早期使用者對 Go 感興趣。一種用于構建系統的垃圾收集、靜態編譯的語言是不尋常的。Go 提供的并行性和并發性的原生支持,使其能夠充分發揮當時正在成為主流的多核機器的優勢。自帶的二進制文件和簡單的交叉編譯使部署變得更加容易。當然,谷歌這個名稱也是一大亮點。


    但是為什么用戶會留下來?為什么 Go 在很多其他語言項目還沒有開發出來的時候,它就變得如此流行了呢?我們認為,語言本身只是答案的一小部分。完整的故事應該包括整個 Go 環境:庫、工具、約定和軟件工程的整體方法,這些都支持用該語言編程。所以,在語言設計方面,最關鍵的決策是讓 Go 能夠更好地適應大型軟件工程,并且能夠吸引有相同想法的開發人員。


    在本文中,我們將會回顧那些我們認為對 Go 的成功負有最大責任的設計決策,并探討這些設計決策如何不僅適用于語言,而且適用于更廣泛的環境。很難將具體決策中的貢獻分開,因此本文不應被視為一種科學的分析,而是一種對 Go 十多年來的經驗和對用戶反饋作出的最好的詮釋。


    起源


    Go 的誕生源于谷歌構建了大規模分布式系統,在一個由成千上萬的軟件工程師共享的大型代碼庫中工作。我們希望為這種環境設計的語言和工具能夠應對公司和整個行業所面臨的挑戰。隨著開發工作的開展和生產系統的大量部署,這些都帶來了一些挑戰。


    開發規模。在開發方面,谷歌在 2007 年有大約 4000 名活躍的用戶在一個單一的、共享的、多語言(C++、Java、Python)的代碼庫中工作。單一的代碼庫使它很容易修復,例如,內存分配器中的問題會讓主 Web 服務器變慢。但是在使用庫的時候,由于很難找到一個包的所有依賴關系,所以很容易在不知不覺中破壞了一個以前未知的客戶端。


    另外,在我們使用的現有語言中,導入一個庫可能會導致編譯器遞歸加載所有導入的庫。在 2007 年的一次 C++ 編譯中,我們觀察到,(在 #include 處理后)傳遞一組總共 4.2MB 的文件時,編譯器讀取了超過 8GB 的數據,在一個已經很大的程序上,擴展系數幾乎達到 2000。如果為編譯一個給定的源文件而讀取的頭文件的數量隨著源樹線性增長,那么整個源樹的編譯成本就會呈平方增長。


    為了彌補速度的減慢,我們開始研究一個新的、大規模并行和可緩存的編譯系統,它最終成為開源的 Bazel 編譯系統。我們認為,光靠語言本身是遠遠不夠的。


    生產規模。在生產方面,谷歌運行的是規模非常龐大的系統。例如,在 2005 年 3 月,Sawzall 日志分析系統的一個擁有 1500 塊 CPU 的集群處理了 2.8PB 的數據。2006 年 8 月,谷歌的 388 個 Big-table 服務集群由 24500 個獨立的 Tablet 服務器組成,其中一組 8069 個服務器每秒處理 120 萬個請求。


    不過,像業界其他公司一樣,谷歌也在致力于編寫高效率的程序,以便充分發揮多核系統的優勢。我們的很多系統都必須在一臺機器上運行同一個二進制文件的多個副本,這是由于現有的多線程支持繁瑣且性能低下。龐大的、固定大小的線程棧,重量級的棧開關,以及用于創建新線程和管理它們之間的交互的笨拙語法,都使得使用多核系統變得更加困難。但是顯然,在服務器中,內核的數量只會越來越多。


    我們還認為,語言自身能夠提供易于使用的輕量級的并發性原語。我們也在這些額外的內核中看到了一個機會:垃圾收集器可以在一個專用的內核上與主程序并行地運行,這樣可以減少它的延遲。


    我們想知道,為應對這些挑戰而設計的語言可能會是什么樣子的,答案就是 Go。Go 的流行,一定程度上是因為所有的科技行業都要面對這樣的挑戰。云計算提供商使得最小型企業也可以將目標鎖定在大規模的生產部署上。盡管大部分公司沒有數千名雇員編寫代碼,但是如今幾乎每個公司都依靠著數以千計的程序員完成的大量開源基礎設施。


    本文的其余部分將探討具體的設計決定如何解決這些開發和生產的擴展目標。我們從核心語言本身開始,向外擴展到周圍的環境。我們不打算全面地介紹這門語言。關于這一點,可以參閱 Go 語言規范或者《Go 編程語言》(The Go Programming Language)之類的書籍。



    一個 Go 程序是由一個或多個可導入的包組成的,每個包都包含一個或多個文件。圖 1 中的 Web 服務器展示很多有關 Go 的包系統設計的重要細節:



    圖 1:Go Web 服務器


    該程序啟動了一個本地的 Web 服務器(第 9 行),它通過調用 hello 函數來處理每個請求,hello 函數用消息“hello, world”(第 14 行)進行響應。


    與許多語言相同,一個包使用明確的 import 語句導入另一個包(第 3-6 行),但與 C++ 的文本 #include 機制不同。不過,與大多數語言不同的是,Go 安排每個 import 只讀取一個文件。例如,fmt 包的公共 API 引用了 io 包的類型:fmt.Fprintf 的第一個參數是 io.Writer 類型的接口值。在大多數語言中,處理 fmt 的 import 的編譯器也會加載所有的 io 來理解 fmt 的定義,這可能又需要加載額外的包來理解所有 io 的定義。一條 import 語句可能最終要處理幾十甚至幾百個包。


    Go 采用與 Modula-2 相似的方式,將編譯后的 fmt 包的元數據包含了了解其自身依賴關系所需的一切,例如 io.Writer 的定義,從而避免了這種工作。因此,import "fmt" 的編譯只讀取一個完全描述 fmt 及其依賴關系的文件。此外,在編譯 fmt 時,可以一次性實現這種扁平化,這樣就可以避免每次導入時的多次加載。這種方式減少了編譯器的工作量,加快了構建速度,為大規模的開發提供了便利。此外,包的導入循環是不允許的:由于 fmt 導入 ioio 就不能導入 fmt,也不能導入任何其他導入 fmt 的東西,即使是間接的。這也降低了編譯器的工作量,確保了在單個單獨編譯的包的級別上對某個特定的構建進行拆分。這也使我們可以進行增量式的程序分析,即使在執行測試之前,我們也會執行這種分析來捕捉錯誤,如下所述。


    導入 fmt 并不能使 io.Writer 這個名字對客戶端可用。如果主包想使用 io.Writer 這個類型,那么它就必須為自己導入“io”。因此,一旦所有對 fmt 限定名稱的引用被從源文件中刪除——例如,如果 import "fmt" 調用被刪除,import "fmt" 語句就可以安全地從源文件中刪除,而無需進一步分析。這個屬性使得自動管理源代碼中的導入成為可能。事實上,Go 不允許未使用的導入,以避免將未使用的代碼鏈接到程序中而造成的臃腫。


    導入路徑是帶引號的字符串字面,這使其解釋具有靈活性。斜線分隔的路徑在導入時標識了 import 的包,但隨后源代碼會使用在包聲明中聲明的短標識符來引用該包。例如,import "net/http" 聲明了頂層名稱 http,提供對其內容的訪問。在標準庫之外,包由以域名開頭的類似 URL 的路徑來識別,如 import "github.com/google/uuid"。我們將在后面對這種包有更多的介紹。


    作為最后一個細節,注意 fmt.Fprintfio.Writer 這兩個名字中的大寫字母。Go 對 C++ 和 Java 的 public、private 和 protected 概念和關鍵字的模擬是一種命名慣例。帶有大寫字母的名字,如 PrintfWriter,是“導出的”(公共的)。其他的則不是。基于大小寫的、編譯器強制執行的導出規則適用于常量、函數和類型的包級標識符;方法名稱;以及結構域名稱。我們采用這一規則是為了避免在公共 API 中涉及的每一個標識符旁邊都寫上一個像 export 這樣的關鍵字的語法負擔。隨著時間的推移,我們已經開始重視查看標識符是否在包之外可用或在其每一次使用時純粹是內部的能力。


    類型


    Go 提供了一套常見的基本類型。布爾,大小整數,如 uint8int32,非大小 intuint(32 或 64 位,取決于機器大小),以及大小浮點數和復數。它提供了指針、固定大小的數組和結構,其方式類似于 C 語言。它還提供了一個內置的字符串類型,一個稱為 map 的哈希表,以及稱為 slices 的動態大小的數組。大多數 Go 程序都依賴于這些,而沒有其他特殊的容器類型。


    Go 不定義類,但允許將方法綁定到任何類型,包括結構體、數組、切片、映射,甚至是基本類型,如整數。它沒有類型層次結構;我們認為繼承性往往會使程序在成長過程中更難適應。相反,Go 鼓勵類型的組合。


    Go 通過其接口類型提供面向對象的多態性。就像 Java 接口或 C++ 的抽象虛擬類一樣,Go 接口包含一個方法名稱和簽名的列表。例如,前面提到的 io.Writer 接口被定義在 io 包中,如圖 2 所示。



    圖 2:io 包的 Writer 接口


    Write 接收一段字節,然后返回一個整數和可能的錯誤。與 Java 和 C++ 不同的是,任何 Go 類型如果擁有與某個接口相同的名稱和簽名的方法,都可以被視為實現了該接口,而無需顯式聲明它是這樣做的。例如,os.File 類型有一個簽名相同的 Write 方法,因此它實現了 io.Writer,所以不需要像 Java 的“implements”注釋那樣的顯式信號。


    不要把這些接口當作一個復雜類型層次結構的基礎塊,而是要避免在接口和實現之間的顯式關聯,這樣,Go 程序員就可以定義小型、靈活、通常是臨時性的接口。它鼓勵捕捉開發過程中出現的關系和操作,而不是需要提前計劃和定義它們。這對大型程序尤其有幫助,因為在剛開始開發時,最終的結構是很難看清楚的。去除聲明實現的簿記,鼓勵使用精確的、只有一種或兩種方法的接口,如 WriterReaderStringer(類似于 Java 的 toString 方法)等,這些接口普遍存在于標準庫中。


    初次學習 Go 的開發人員常常擔心一個類型會意外地實現一個接口。雖然構建假設很容易,但在實踐中,不太可能為兩個不兼容的操作選擇相同的名稱和簽名,而且我們從未在實際的 Go 程序中看到過這種情況發生。


    并發性


    當我們開始設計 Go 的時候,多核計算機已經開始廣泛使用,但線程在所有流行的語言和操作系統中仍然是一個重量級的概念。創建、使用和管理線程的難度使其不受歡迎,限制了對多核 CPU 全部功能的使用。解決這一矛盾是創建 Go 的主要動機之一。


    Go 語言本身包含了多個并發控制線程的概念,稱為 goroutines,在一個共享地址空間中運行,并有效地復用到操作系統線程上。對阻塞操作的調用,如從文件或網絡中讀取,只阻塞進行該操作的 goroutine;該線程上的其他 goroutine 可能會被移到另一個線程,以便在調用者被阻塞時繼續執行。goroutine 開始時只有幾千字節的堆棧,它可以根據需要調整大小,無需程序員參與。開發人員將 Goroutines 作為一種豐富的、廉價的結構化程序的原語。對于一個服務器程序來說,擁有數千甚至數百萬個 goroutines 是很平常的,因為它們的成本要遠低于線程。


    例如,net.Listener 是一個帶有 Accept 方法的接口,可以監聽并返回新進入的網絡連接。圖 3 顯示了一個接受連接的函數 listen,并為每個連接啟動一個新的 goroutine 來運行服務函數。



    圖 3:一個 Go 的網絡服務器。


    listen 函數主體中的無限 for 循環(第 22-28 行)調用 listener.Accept,它返回兩個值:連接和一個可能的錯誤。假設沒有錯誤,go 語句(第 27 行)在一個新的 goroutine 中啟動其參數——函數調用 serve(conn),類似于 Unix shell 命令的后綴 &,但在同一個操作系統進程中。要調用的函數及其參數在原 goroutine 中被評估;這些值被復制以創建新 goroutine 的初始堆棧框架。因此,程序為每個進入的網絡連接運行一個獨立的 serve 函數實例。對 serve 的調用一次處理一個給定連接上的請求(第 37 行對 handle(req) 的調用沒有以 go 為前綴);每次調用都可以阻塞而不影響對其他網絡連接的處理。


    在幕后,Go 的實現使用了高效的復用操作,比如 Linux 的 epoll,它可以處理并發的 I/O 操作,但用戶是看不到的。Go 的運行庫呈現的是阻塞式 I/O 的抽象,其中每個 goroutine 都是按順序執行的,無需回調,這很容易推理。


    在創建了多個 goroutine 之后,一個程序必須經常在它們之間進行協調。Go 提供了通道,允許 goroutine 之間進行通信和同步:通道是一個單向的、尺寸有限的管道,在 goroutine 之間傳輸類型化的信息。Go 還提供了一個多向 select 原語,可以根據通信的進行來控制執行。這些想法改編自 Hoare 的 "通信順序過程 "19 和早期的語言實驗,特別是 Newsqueak、Alef 和 Limbo。


    圖 4 展示了另一個版本的 listen,它是為了限制任何時候的連接數量而寫的。



    圖 4:一個 Go 網絡服務器,限制為 10 個連接。


    這個版本的 listen 首先創建了一個名為 ch 的通道(第 42 行),然后啟動了一個由 10 個服務器 goroutines 組成的池(第 44-46 行),它們接收來自這個單一通道的連接。當新的連接被接收時,listen 使用 send 語句 ch < - conn(第 53 行)在 ch 上發送每個連接。一個服務器執行接收表達式 < - ch(第 59 行),完成通信。通道的創建沒有空間來緩沖正在發送的值(Go 中的默認值),所以在 10 個服務器忙完前 10 個連接后,第 11 個 ch < - conn 將被阻塞,直到一個服務器完成對服務的調用并執行新的接收。被阻塞的通信操作對監聽器產生了隱性的背壓,阻止它接受一個新的連接,直到它放棄前一個連接。


    請注意,在這些程序中缺乏互斥或其他傳統的同步機制。在通道上進行的數據值通信可以作為同步的一部分;按照慣例,在通道上發送數據會將所有權從發送方傳給接收方。Go 有提供互斥、條件變量、信號燈和原子值的庫,供低級別的使用,但通道往往是更好的選擇。根據我們的經驗,人們對消息傳遞——利用通信在 goroutine 之間轉移所有權——的推理比對互斥和條件變量的推理更容易、更正確。早期的口號是:“不要通過共享內存來交流,而是通過交流來共享內存”。


    Go 的垃圾收集器大大簡化了并發 API 的設計,消除了關于哪個 goroutine 負責釋放共享數據的問題。與大多數語言一樣(但與 Rust 不同),可變數據的所有權不由類型系統靜態跟蹤。相反,Go 集成了 TSAN,為測試和有限的生產使用提供了一個動態競爭檢測器。


    安全性


    任何新語言的部分原因都是為了解決以前語言的缺陷,比如 Go,它涉及影響網絡軟件安全的安全問題。Go 刪除了在 C 和 C++ 程序中造成許多安全問題的未定義行為。整數類型不會自動相互牽制。空指針取消引用和越界的數組和片索引會導致運行時異常。不存在指向棧框架的迷途指針。任何可能超出其棧框架的變量,例如在閉包中捕獲的變量,將被移到堆中。在堆中也沒有迷途指針;使用垃圾收集器而不是手動內存管理可以消除使用后的錯誤。當然,Go 并沒有解決所有問題,有些東西被遺漏了,也許應該得到解決。例如,整數溢出可以被定義為運行時錯誤,而不是定義為繞過。


    由于 Go 是一種用于編寫系統的語言,它可能需要破壞類型安全的機器級操作,因此它能夠將指針從一種類型脅迫到另一種類型,并執行地址運算,但只能通過使用 unsafe 包及其受限制的特殊類型 unsafe.Pointer。必須注意保持對類型系統的違反與垃圾收集器兼容——例如,垃圾收集器必須始終能夠識別一個特定的字是一個整數還是一個指針。在實踐中,unsafe 包很少出現:安全 Go 是相當有效的。因此,看到 import "unsafe" 是一個信號,可以讓我們更仔細地檢查源文件是否存在安全問題。


    與 C、C++ 之類的語言相比,Go 的安全性更好,更適合用于加密和其他重要的安全代碼。在 C 和 C++ 中,一個微小的錯誤,比如一個越界的數據索引,就會造成敏感數據的泄漏或者被遠程執行,但是在 Go 中,它會造成運行時的異常,從而使程序停止,極大地限制了潛在的影響。Go 提供了一個完整的加密庫套件,其中包含了 SSL/TLS 的支持;標準庫包含 HTTPS 客戶端和服務器,可用于生產環境。實際上,Go 的安全性、性能和高品質庫的結合使它成為了一個現代安全工作的熱門試驗場。比如,Let's Encrypt 是一家免費提供證書的機構,它依靠 Go 來提供生產服務,并在最近跨越了一個里程碑:簽發了 10 億份證書。


    完整性


    Go 在語言、庫和工具層面提供了現代開發所需的核心部分。這就需要小心翼翼地平衡,既要增加足夠多的“開箱即用”功能,又不能增加太多,以至于我們自己的開發過程因為要支持太多的功能而陷入困境。


    該語言提供了字符串、哈希圖和動態大小的數組作為內置的、易于使用的數據類型。如前所述,這些對于大多數 Go 程序來說已經足夠了。其結果是 Go 程序之間有了更大的互操作性——例如,沒有競爭性的字符串或哈希圖的實現來分割包的生態系統。Go 包含的 goroutines 和 channel 是另一種形式的完整性。這些功能提供了現代網絡程序中所需要的核心并發功能。直接在語言中提供這些功能,而不是在庫中提供,這樣可以更容易地調整語法、語義和實現,使其盡可能地輕量和易于使用,同時為所有用戶提供統一的方法。


    該標準庫包括一個生產就緒的 HTTPS 客戶端和服務器。對于在互聯網上與其他機器互動的程序來說,這一點至關重要。直接滿足這一需求可以避免額外的碎片化。我們已經看到了 io.Writer 接口;任何輸出數據流都按慣例實現了這個接口,并與所有其他 I/O 適配器進行互操作。圖 1 的 ListenAndServe 調用,作為另一個示例,期望有一個 http.Handler 類型的第二個參數,其定義如圖 5 所示。參數 http.HandlerFunc(hello) 通過調用 hello 實現其 ServeHTTP 方法。該庫創建了一個新的 goroutine 來處理每個連接,就像本文“并發性”部分中的監聽器示例一樣,所以處理程序可以用簡單的阻塞風格來編寫,服務器可以自動擴展以處理許多同步連接。



    圖 5:net/http 包的處理程序接口


    http 包還提供了一個基本的調度器,它本身就是 Handler 的另一種實現,它允許為不同的 URL 路徑注冊不同的處理程序。將 Handler 確立為約定俗成的接口,使得許多不同類型的 HTTP 服務器中間件能夠被創建并相互操作。我們不需要將所有這些實現添加到標準庫中,但我們確實需要建立一個允許它們一起工作的接口。


    標準 Go 發行版還提供了對交叉編譯、測試、剖析、代碼覆蓋率、模糊處理等的集成支持。測試是另一個領域,在這個領域中,建立關于核心概念的協議——例如什么是測試用例以及如何運行——使得創建的自定義測試庫和測試執行環境都能很好地互操作。


    一致性


    我們對 Go 的一個目標是讓它在不同的實現、執行環境中,甚至在不同的時間內表現出相同的行為。這種“無聊”的一致性行為使開發人員能夠專注于他們的日常工作,并使 Go 隱退到后臺。


    首先,語言盡可能地規定了一致的結果,即使是錯誤的行為,如空指針解除引用和越界數組索引,正如本文的“安全性”部分所討論的。Go 需要不一致行為的一個例外是對哈希圖的迭代。我們發現,程序員經常不經意地寫下依賴于哈希函數的代碼,導致在不同的架構或 Go 實現上出現不同的結果。


    為了使程序在任何地方都有相同的表現,一種選擇是強制規定一個特定的哈希函數。相反,Go 定義了映射迭代是非確定的。該實現為每個映射使用不同的隨機種子,并從哈希表中的一個隨機偏移量開始對映射進行每次迭代。其結果是,映射在不同的實現中都是不可預知的。代碼不能意外地依賴于實現細節。與此類似,競爭檢測器為調度決策增加了額外的隨機性,創造了更多的機會來觀察競爭。


    一致性的另一個方面是在程序的生命周期內的性能。使用傳統的編譯器而不是 Java 和 Node.js 等語言使用的 JIT 來實現 Go 的決策,在啟動時和短期程序中提供了一致的性能。不存在“慢速啟動”來懲罰每個進程生命周期的前幾秒。對于命令行工具和規模較大的網絡服務器(如 Google App Engine)來說,這種快速的啟動使 Go 成為一個有吸引力的目標。


    一致性的性能包括垃圾收集的開銷。最初的 Go 原型使用了一個基本的、即停即用的垃圾收集器,當然,它在網絡服務器中引入了明顯的尾部延時。今天,Go 使用了一個完全并發的垃圾收集器,暫停時間不到一毫秒,通常僅為幾微秒,與堆的大小無關。最主要的延遲是操作系統向必須中斷的線程傳遞信號所需的時間。


    最后一種一致性是語言和庫隨著時間的推移而產生的一致性。在 Go 誕生的前幾年,我們在每周的發布中都會對它進行修補和調整。用戶在更新到新的 Go 版本時,往往不得不改變他們的程序。自動化的工具減輕了負擔,但手動調整也是必要的。從 2012 年發布的 Go 版本開始,我們公開承諾只對語言和標準庫進行向后兼容的修改,這樣程序在編譯到較新的 Go 版本時,可以在不改變的情況下繼續運行。這一承諾吸引了業界,不僅鼓勵了長期的工程項目,也鼓勵了其他努力,如書籍、培訓課程和第三方軟件包的繁榮生態系統。


    工具輔助開發


    大規模的軟件開發需要大量的自動化和工具化。從一開始,Go 的設計就是為了鼓勵這種工具化,使其易于創建。


    開發人員對 Go 的日常體驗是通過 go 命令進行的。與只編譯或運行代碼的語言命令不同,go 命令為開發周期的所有關鍵部分提供了子命令:go buildgo install 構建和安裝可執行文件,go test 運行測試用例,go get 添加新的依賴。go 命令還提供了對構建細節的編程訪問,例如軟件包圖,從而實現了新工具的創建。


    其中一個工具是 go vet,它可以執行增量的、每次打包的程序分析,可以像緩存編譯的對象文件那樣緩存,實現增量構建。go vet 工具的目的是高精度地識別常見的正確性問題,這樣開發人員就有條件按照它的報告進行處理。簡單的例子包括在調用 fmt.Printf 和相關函數時檢查格式和參數是否匹配,或者診斷對變量或結構域的未使用的寫入。這些不是編譯器錯誤,因為我們不希望僅僅因為發現了一個新的可能的錯誤就停止編譯舊代碼。它們也不是編譯器警告;用戶要學會忽略這些。將這些檢查放在一個單獨的工具中,可以讓它們在開發人員方便的時候運行,而不干擾普通的構建過程。這也使得所有的開發人員都可以使用同樣的檢查,即使是在使用 Go 編譯器的另一種實現,如 Gccgo15 或 Gollvm17。增量方法使得這些靜態檢查足夠有效,我們在 go test 期間自動運行它們,然后再運行測試本身。無論如何,測試是用戶尋找錯誤的時候,而報告往往有助于解釋實際的測試失敗。這個增量框架也可以被其他工具重用。


    分析程序的工具非常有用,但編輯程序的工具就更好了,特別是對于程序的維護方面,很多都是枯燥乏味的,并且已經是成熟的自動化。


    Go 程序的標準布局是通過算法定義的。一個名為 gofmt 的工具將源文件解析為抽象的語法樹,然后使用一致的布局規則將其格式化為源代碼。在 Go 中,在將代碼存儲到源控制中之前將其格式化被認為是一種最佳做法。這種方法使數以千計的開發人員能夠在一個共享的代碼庫中工作,而不需要經常為大括號樣式和其他細節進行爭論,這些都是伴隨著這種大型工作的。更重要的是,工具可以通過對抽象語法形式的操作來修改 Go 程序,然后用 gofmt 的打印機寫出結果。只有實際改變的部分才會被觸及,產生的“差異”與人們的手寫結果是一致的。人們和程序可以在同一個代碼庫中無縫協作。


    為了實現這種方法,Go 的語法被設計為能夠在沒有類型信息或任何其他外部輸入的情況下就可以對源文件進行解析,并且不需要預處理器或其他宏系統。Go 標準庫提供了一些包,允許工具重新創建 gofmt 的輸入和輸出端,同時還有一個完整的類型檢查器。


    在發布 Go 第 1 版(第一個穩定的 Go 版本)之前,我們編寫了一個叫做 gofix 的重構工具,它使用這些包來解析源代碼,重寫樹,并寫出格式良好的代碼。例如,當從映射中刪除一個條目的語法被改變時,我們就使用了 gofix。每次用戶更新到一個新版本時,他們可以在他們的源文件上運行 gofix,自動應用更新到新版本所需的大部分變化。


    這些技術也適用于 IDE 插件和其他支持 Go 程序員的工具——過濾器、調試器、分析器、構建自動程序、測試框架等等的構建。Go 的常規語法、既定的算法代碼布局慣例以及對標準庫的直接支持,使得這些工具的構建比其他方式要容易得多。因此,Go 世界擁有一個豐富的、不斷擴展的、可互操作的工具包。



    在語言和工具之后,用戶如何體驗 Go 的下一個關鍵方面是可用的庫。作為一種分布式計算的語言,Go 中沒有必須發布 Go 軟件包的中央服務器。相反,每個以域名開始的導入路徑都被解釋為一個 URL(有一個隱含的前導 https://),提供遠程源代碼的位置。例如,import "github.com/google/uuid" 可以獲取托管在相應的 GitHub 倉庫的代碼。


    托管源代碼最常見的方式是指向公共的 Git 或 Mercurial 服務器,但私人服務器也同樣得到了很好的支持,作者可以選擇發布一個靜態的文件包,而不是開放對源控制系統的訪問。這種靈活的設計和發布庫的便利性創造了一個繁榮的可導入 Go 包的社區。依靠域名,避免了在扁平的包名稱空間中急于聲明有價值的條目。


    僅僅下載軟件包是不夠的,我們還必須知道要使用哪些版本。Go 將包分組為稱為模塊的版本單位。一個模塊可以為它的一個依賴關系指定一個最低要求的版本,但沒有其他限制。當構建一個特定的程序時,Go 通過選擇最大版本來解決競爭的依賴模塊的所需版本。如果程序的一部分需要某個依賴模塊的 1.2.0 版本,而另一部分需要 1.3.0 版本,Go 會選擇 1.3.0 版本——也就是說,Go 要求使用語義版本劃分,其中 1.3.0 版本必須是 1.2.0 的直接替換。另一方面,在這種情況下,即使 1.4.0 版本可用,Go 也不會選擇它,因為程序中沒有任何部分明確要求使用該較新的版本。這個規則保持了構建的可重復性,并最大限度地減少了因意外破壞新版本所引入的變化而造成的潛在風險。


    在語義版本管理中,一個模塊只能在一個新的主要版本中引入有意的破壞性變化,比如 2.0.0。在 Go 中,從 2.0.0 開始的每個主要版本在其導入路徑中都有一個主要版本后綴,比如 /v2。不同的主版本和其他不同名字的模塊一樣被分開。這種方法不允許出現鉆石依賴性問題,而且在實踐中,它可以適應不兼容的情況,也可以適應具有更精細約束的系統。


    為了提高從互聯網上下載軟件包的構建的可靠性和可重現性,我們在 Go 工具鏈中運行了兩個默認使用的服務:一個是可用 Go 軟件包的公共鏡像,一個是加密簽名的預期內容的透明日志。即使如此,廣泛使用從互聯網上下載的軟件包仍然存在安全和其他風險。我們正在努力使 Go 工具鏈能夠主動識別并向用戶報告脆弱的軟件包。


    總結


    雖然大多數語言的設計都集中在語法、語義或類型的創新上,但 Go 的重點是軟件開發過程本身。Go 語言高效、易學、免費,但我們相信,它的成功之處在于它所采取的編寫程序的方式,特別是多個程序員在一個共享代碼庫上工作時。該語言本身的一個重要屬性,即并發性,解決了 2010 年代隨著多核 CPU 的大量使用而出現的問題。但更重要的是,早期的工作為打包、依賴關系、構建、測試、部署和軟件開發領域的其他工作任務奠定了基礎,這些方面通常在語言設計中并不重要。


    這些想法吸引了志同道合的開發人員,他們重視的結果是:容易并發、明確的依賴關系、可擴展的開發和生產、安全的程序、簡單的部署、自動代碼格式化、工具輔助開發等等。這些早期的開發人員幫助普及了 Go,并播種了最初的 Go 包生態系統。他們還推動了該語言的早期發展,例如,將編譯器和庫移植到 Windows 和其他操作系統上(最初的版本只支持 Linux 和 MacOS X)。


    并非所有的人都會喜歡——比如,有些人反對該語言省略了繼承和泛型等常見功能。但是 Go 的開發導向的理念足夠吸引人,也足夠有效,以至于社區在保持最初推動 Go 存在的核心原則的同時,也得到了蓬勃發展。在很大程度上,多虧了這個社區和它所建立的技術,Go 如今已成為現代云計算環境的一個重要組成部分。


    自 Go 第一版發布以來,該語言幾乎被凍結。然而,工具已經大大擴展,有了更好的編譯器,更強大的構建和測試工具,以及改進的依賴性管理,更不用說支持 Go 的大量開源工具了。然而,變化正在到來。2022 年 3 月發布的 Go 1.18,它包含了對語言的真正改變的第一個版本,一個被廣泛要求的改變:首次實現了參數化多態。我們將任何形式的泛型排除在原始語言之外,因為我們敏銳地意識到,它很難設計好,而且在其他語言中,往往是復雜性而不是生產力的來源。在 Go 的第一個十年中,我們考慮了很多設計,但直到最近才找到一個我們認為很適合 Go 的設計。在堅持一致性、完整性和社區原則的前提下進行如此大的語言變革,對于這種方式來說,將是一個巨大的挑戰。


    作者介紹:


    本文作者為:Russ Cox、Robert Griesemer、Rob Pike、Ian Lance Taylor、Ken Thompson,均為谷歌公司軟件工程師,參與了 Go 項目。Rob Pike、Ken Thompson 已退休。


    原文鏈接:


    https://m-cacm.acm.org/magazines/2022/5/260357-the-go-programming-language-and-environment/fulltext

    《智齒》是筆者近幾年來最期待的兩部香港電影之一,另一部是《風林火山》。這部2018年就拍完的電影終于來了,久違的港片癲狂,久違的黑暗末世,看過之后即震撼又百感交集。


    《智齒》的制作班底堪稱豪華,導演是極具個人風格的鄭保瑞,拍得又是他最拿手是犯罪題材,幕后班底都是銀河映像的大拿,制片是葉偉信,編劇是歐健兒,美術設計是麥國強,那個如地獄般的布景就出自他手,極大提升了電影質感。此片的配樂是川井憲次,主演是林家棟、劉雅瑟、李淳和池內博之(葉問1里的日本將軍)。


    這部電影年初入選了柏林影展的“特別放映”單元,然后備受世界各大影展青睞,在海外取得了極大的關注。



    《智齒》講了一個怎樣的故事?



    腐爛的都市角落,失控的地獄邊緣,垃圾一樣的生命。一邊是飛快的進化,一邊是脫節的螻蟻,繁榮與污穢,匱乏與疏離,絕望與希望,相伴相生。病態的都市導致病態的人性,一系列的扭曲瘋狂、血腥殺戮、悲劇宿命在迷幻的香港雨夜陸續上演。在癲狂的鋼鐵叢林中,病態的人們該如何面對彼此?


    這部電影改編自雷米的同名小說,原著是發生在一個城鄉結合部的黑暗故事,鄭保瑞將故事移植到架空的香港都市-更加復雜的環境,更加扭曲的人性,更加濕濡的氣候,而香港作為一個國際都市,在地緣上正是放大版的結合部,這一切都讓電影《智齒》在故事層面做了一次全面升級。


    要深度理解這部影片最好從“宏觀的都市生態”“微觀的人物掙扎”這兩個方面入手。這就牽扯到鄭保瑞導演一貫的表達主題,以及對他影響巨大的銀河映像。



    一:鄭保瑞與銀河映像



    鄭保瑞最早跟隨林嶺東起家,所以也繼承了林嶺東電影中關注社會問題的特點,對人性的暴力與黑暗刻畫得尤其出彩。


    05年鄭保瑞拍了《怪物》,塑造了一個隱藏在大廈電梯井里偷孩子的母親怪;06年拍了《狗咬狗》,陳冠希與李燦森上演了一場人與獸的生存大戰,這是他前期最有作者性的兩部電影,《智齒》就是這條脈絡的延伸。



    后來,他被杜琪峰賞識,加入銀河映像,與葉偉信搭檔多年,拍出的《意外》與《殺破狼2》等,具有明顯的銀河映像風格。


    銀河映像是杜琪峰與韋家輝、游達志共同創辦的廠牌,匯集了一批獨具特色的港片精英。作為香港電影最后一個名廠牌,銀河映像一直以“冷、硬、黑、狠”的風格著稱,攝影與配樂也極有格調。


    我們在《智齒》的故事中能看到很多對銀河映像經典影片的致敬,比如《神探》、《PTU》、《暗花》,這個故事可視為鄭保瑞風格與銀河映像的一次匯總升級。



    大都市社會的病態,人際關系的疏離與異化,底層的血腥暴力與純真的人性之善等是鄭保瑞一直關注的主題,《智齒》涵蓋以上所有內容。銀河映像里經常出現的宿命論,對信仰的拷問等也都融入到了故事中。


    二:影片的標題設計-奠定基調、提煉內涵



    《智齒》的完整標題是:智齒LIMBO。智齒二字縮在一側,占最大篇幅的是由螞蟻組成的英文LIMBO。



    英文LIMBO意為:地獄的邊境,放置丟棄物的場所,或中間的不確定狀態。在基督教語境里,limbo指不信仰基督的人,去世后靈魂暫時的歸宿地,等待救世主降臨拯救他們

網站首頁   |    關于我們   |    公司新聞   |    產品方案   |    用戶案例   |    售后服務   |    合作伙伴   |    人才招聘   |   

友情鏈接: 餐飲加盟

地址:北京市海淀區    電話:010-     郵箱:@126.com

備案號:冀ICP備2024067069號-3 北京科技有限公司版權所有