Rust比你想象的還要復雜!你知道嗎?
你可能被告知Rust是復雜的,但這不是真的,實際上Rust比你想象的要復雜得多!在這篇文章中,將向你展示Rust隱藏其復雜性的不同方式。
首先,讓我們從一個看似簡單的hello world程序開始。
圖片
程序看起來很簡單,只是一個主函數(shù)和一個打印語句。但這不是編譯器看到的,注意println!不是一個函數(shù),它是一個宏,這意味著它在編譯時擴展成更復雜的代碼。
在這個例子中,還有一些不是由宏生成的隱藏代碼,這個隱藏代碼我們可以使用一個叫做cargo expand的工具:
圖片
cargo expand是一個強大的開發(fā)工具,它向我們展示了編譯器在展開宏和應用編譯器轉(zhuǎn)換后看到的完整rust代碼。
如果我們運行cargo expand,hello world示例將展開為以下代碼:
圖片
讓我們一步一步來分析代碼,前三行導入Prelude,它是一組在標準庫中定義的常用項,包括常用的Trait、Box智能指針、Option和Result枚舉等。
第一行啟用了prelude_import特性,通常表示不穩(wěn)定或不斷發(fā)展的功能。prelude_import特性允許Rust團隊可以在編譯器內(nèi)部修改處理prelude_import的方式,從而在不影響用戶代碼的情況下實現(xiàn)潛在的優(yōu)化。
第二行是一個特殊屬性,確保Prelude中的各項在你的整個代碼庫中可用。
第三行是將Rust 2021版本中的所有項導入到當前作用域內(nèi)。每3年升級一次的這種Rust版本是一種引入突破性變化的機制,而不是強迫每個人每3年必須升級一次。新的版本會引入新的功能和語法,但這些都是可選擇的,這樣你就不必升級每個crate,可以選擇對你有用的新特性,且一個版本中的crate必須與其他版本中編譯的crate無縫互操作。這意味著你可以獲得新特性的好處,而不會破壞現(xiàn)有的代碼。
接下來的兩行告訴編譯器使用Rust標準庫宏。use是一個屬性,表示允許使用標準庫中的宏,而不需要顯式導入。extern crate std聲明了對標準庫的依賴,大多數(shù)crate不需要extern,因為編譯器會自動鏈接Cargo.toml中定義的依賴項。這里使用extern crate std行來支持舊版本的println!宏。
讓我們看一下main中的代碼,這是println!宏展開成的代碼,_print是rust的IO模塊中的底層函數(shù),處理實際的字符串打印。
format_args!是另一個宏,可以處理任何格式化參數(shù)的字符串格式化。它是一個特殊的宏,直接內(nèi)置于編譯器中,實際上不會擴展為rust代碼。
讓我們看一下_print的定義:
圖片
我們看到它調(diào)用了一個更底層的函數(shù)print_to,它接受格式化的字符串參數(shù),一個緩沖區(qū),在這個例子中是標準輸出,一個標簽,然后將字符串打印到緩沖區(qū)并處理任何可能的錯誤。
回到我們的hello world示例,我們看到_print在內(nèi)部作用域中被調(diào)用,這是Rust處理宏展開的一部分。內(nèi)部作用域中確保宏展開期間創(chuàng)建的臨時變量或表達式不會泄漏到周圍的作用域中,并有助于防止意外的變量遮蔽或生命周期問題。
現(xiàn)在,為了運行這個擴展代碼,需要切換到nightly編譯器,并啟用print internal特性:
圖片
因為這段代碼使用了內(nèi)部編譯器特性,而這些特性不是rust穩(wěn)定的公共API的一部分?,F(xiàn)在我們可以運行我們的hello world示例,正如你所看到的,rust在幕后做了很多工作,即使是簡單的程序,比如println!宏,也被擴展成了更復雜的優(yōu)化代碼。
總結(jié)
Rust會自動導入常用的項并鏈接到標準庫,甚至簡單的操作都有我們看不到的安全檢查和優(yōu)化,Rust管理復雜性的方法遠遠超出了我們的hello world示例。
從使用所有權(quán)系統(tǒng)自動管理內(nèi)存,到使用Result類型和問號操作符簡化錯誤處理,再到提供包管理器、檢查器和格式化器等內(nèi)置工具。Rust一直致力于管理復雜性,讓我們的編程更輕松。