網易首頁 > 網易號 > 正文 申請入駐

Rust 的獨特之處不只內存安全:來自自主機器人的啟示

0
分享至


作者 | Andy Brinkmeyer

譯者 | 平川

在過去的幾年里,我一直從事自主移動機器人領域的研究,本文中的許多示例就源自該領域。我寫這篇文章是為了探討一下,除了眾所周知的內存安全保障之外,Rust 還有哪些獨特之處。具體來說,就是該語言如何幫助開發者從一開始就編寫出更正確的軟件,不犯常見的錯誤,使生成的代碼有更強的防錯能力。

不只是內存安全

在我與開發者們討論 Rust 時,一個常見的情況是:那些沒有投入大量時間研究這門語言的人往往會對其嗤之以鼻,而這通常發生在短暫且不成功的首次嘗試之后。然而,根據我的經驗,那些能夠克服初期學習曲線并將其應用于實際項目的開發者,往往會對 Rust 非常欣賞。我至今仍然記得,兩年半以前,當我們要啟動一個大型的新項目時,將有不同編程語言背景的人聚集到一起在一個純 Rust 代碼庫上進行開發的情景。

其中有一位 C++ 背景的同事,最初兩周每天都在向我抱怨。但大約三四周后,情況發生了轉變。他開始真正喜歡上了這門語言,現在甚至表示再也不想回頭了。

這個例子或許能說明,為什么 Rust 在 Stack Overflow 的調查中始終名列最受歡迎的編程語言之首。雖然毫無疑問,內存安全性非常重要,而且我在自己的項目中也受益匪淺,但這還不是全部原因。Rust 讓編寫從一開始就正確的軟件變得容易許多,它能有效降低開發者引入錯誤的可能性,而且生成的代碼在出現故障時會表現出更強的韌性。

內核簡單

從本質上講,Rust 是一種相對比較簡單的語言。人們普遍認為, Rust 極難,學習曲線非常陡峭。這種看法雖然有一定的道理,但仔細考察其基本概念就會發現,該語言的核心在于數據類型以及操作這些數據類型的函數。其他語言中許多會增加復雜性的概念在 Rust 中根本不存在:沒有垃圾回收、沒有類、沒有繼承、沒有傳統的面向對象編程、沒有空指針、沒有函數重載,也沒有類型強制轉換。這種極簡主義的設計理念,使得一些開發者將 Rust 的使用體驗比作 C 語言,或者更新的 Zig 語言。

枚舉:不只是整數

Rust 中的枚舉與其他語言中的枚舉有著根本性的區別,因為枚舉變體可以存儲數據。枚舉的每個變體可以存儲與其他變體不同的特定數據,也可以完全不存儲任何數據。這一概念類似于 TypeScript 中的帶標簽聯合體,但在 Rust 中,它作為一項統一的一等語言特性而存在。

模式匹配與窮盡性

要訪問枚舉中的數據,開發者必須使用和其他語言中 switch 語句類似的結構來匹配該枚舉。通過匹配表達式,可以檢查枚舉值實際屬于哪個變體,并執行相應的邏輯。關鍵在于,在特定的匹配分支內,編譯器僅允許訪問該變體關聯的數據。匹配必須窮盡,也就是說必須顯式處理每個變體,或者通過通配模式進行處理,以防止開發者無意中遺漏了某個情況的處理。

可選數據建模

在軟件系統中,可選數據無處不在。許多編程語言通過空指針、nil 值或專門的標準庫類型來實現這一概念。在 Rust 中,可選數據通過 Option 類型來實現,這是一個定義在標準庫中的簡單枚舉類型,任何開發者都能在一分鐘內實現它。Option 枚舉有兩種變體:None(不包含數據)和 Some(T)(封裝一個泛型值)。

}

得益于枚舉的數據訪問規則,可選數據被保護了起來,避免了意外訪問。例如,在我的機器人研究工作中,機器人可能處于執行任務狀態,也可能處于空閑狀態。若使用 Option 來建模這種行為,則在訪問任務數據之前必須對值進行匹配,以確保代碼僅對實際存在的任務進行操作。

使用枚舉實現狀態機

狀態管理在軟件開發中無處不在。Rust 提供了一種直觀的方法,即使用枚舉來進行狀態建模,其中每個變體代表一種狀態,并封裝了與該狀態相關的數據。對于機器人而言,其狀態可能包括 Uninitialized(意識到機器人的存在)、Initialized(已知位置)以及 ExecutingJob(同時擁有位置和任務信息)。

}

這種方法提供了雙向保護:數據處于錯誤狀態時受到保護,可防止意外訪問;而要轉換到新狀態時,必須提供必要的數據,這樣就不會構建出無效的狀態。編譯器會自動強制執行這些約束。

所有權:只能有一個

所有權是 Rust 中的一個核心概念,在其他主流語言中沒有與之直接對應的概念。其規則非常簡單:在任何時候,Rust 中的每個值都只有一個所有者,不多也不少。編譯器會在編譯時通過靜態分析來強制執行這條規則。

生命周期管理

Rust 能夠精確追蹤值的生命周期,因為所有權明確標識了每個值的所有者,以及所有權何時超出作用域。當所有者超出作用域時,它所擁有的值會被丟棄,內存會被釋放,其他資源也會被釋放。

所有權轉移

所有權可以通過賦值或函數調用進行轉移或“移動”。當數據被移動時,所有權會從原始所有者轉移到新所有者。原始所有者將無法再訪問該值。這種機制可以防止我所說的“雙重使用”情況,即本應唯一存在的實體被意外地多次使用。

let x = y // 所有權從 y 移動到 x

在機器人系統中,一個任務每次只能由一個機器人執行一次。假設有一個函數簽名,其中有一個 job 參數按值傳遞一個 Job 對象;換言之,該函數獲得了該任務的所有權。當把任務分配給機器人時,所有權便從調用代碼轉移到了函數中。

嘗試將同一個任務分配給兩個不同的機器人會引發編譯時錯誤,因為第一次賦值操作已將所有權從原始變量轉移了出去。所有權轉移就是這樣在編譯時優雅地防止了重復使用。

error[E0382]: use of moved value: `new_job`

資源管理不限于內存

值的生命周期不僅能管理內存。通過 Drop trait 掛接 drop 事件,可以為超出作用域的值編寫自定義行為。在機器人領域,像狹窄的走廊這樣的物理空間可能一次只允許存在一臺機器人。ZoneAccess 令牌類型可以表示進入此類區域的權限。由于 ZoneAccess 既不能被復制也不能被克隆,因此同一時間只能存在一個令牌。

}

通過為 ZoneAccess 實現 Drop 來自動釋放該區域,便無需手動處理所有可能需要釋放該區域的代碼路徑,例如機器人斷開連接、狀態變更或其他任何場景。當擁有 ZoneAccess 的機器人超出作用域時,令牌也會隨之超出作用域,資源隨之釋放。這種模式能夠自動防止所有代碼路徑中的資源泄漏,從而極大地簡化除內存之外的實際資源的管理。

借用:安全地引用數據

如果數據只能被擁有,那么語言的功能將受到極大的限制。借用提供了一種在不擁有數據所有權的情況下安全地引用數據的方式。借用規則規定,在任何給定的時刻,一個值可以存在一個可變引用,或者存在任意數量的不可變引用,但不能同時存在這兩種引用。

let x = &y;

這條規則對于內存安全來說至關重要,因為它消除了多個代碼位置訪問同一變量時可能引發的數據競爭?勺兘栌迷试S修改底層數據;不可變借用僅允許讀取它。重要的是,借用不會轉移所有權:只要引用存在,原始所有者仍保留所有權。

生命周期:這個數據是否還可以安全地使用?

生命周期與借用機制協同工作,用于判斷在程序執行的某個特定時刻,借用是否仍然有效。引用的生命周期不能超過其所引用的值的生命周期;這條規則對于內存安全來說至關重要,因為它能防止值被釋放后仍對其進行引用。

可變生命周期從創建時開始,到銷毀時結束。編譯器利用這些信息進行生命周期檢查。大多數情況下,無需顯式標注生命周期,因為編譯器會自動推斷出來。但在某些情況下,則需要顯式標注,例如使用 'a 這樣的語法來表示命名生命周期。

將協議嵌入類型

將所有權、借用和生命周期結合使用,可以實現一種特別有趣的功能:在編譯時將運行時協議嵌入到類型中。Rust 的主要序列化庫 Serde 便是一個極具說服力的例子。

}

序列化器模式

假設有一個用于序列化單個值的序列化器類型。該 Serializer 實現了針對各種值類型(整數、浮點數、枚舉和結構體)的方法。serialize_struct 函數接受按值傳遞的 self,這意味著它會消耗該序列化器實例。調用此函數后,原始序列化器將無法再次訪問。該函數返回一個 SerializeStruct,它是從通用序列化器轉換而來的一個序列化器,專門用于結構體序列化。

}

SerializeStruct 類型實現了兩個方法:erialize_field 方法接受 self 的不可變引用,可針對多個字段反復調用;end 方法則用于結束序列化過程。end 函數承擔著重要的職責,它可能會在 JSON 中寫入一個閉合大括號,或計算包含結構體字節大小的文件頭。許多序列化庫的文檔都會警告用戶,不要在調用 end 之后再調用其他序列化函數,因為此類違規操作可能會導致程序崩潰或運行時錯誤。

在 Rust 中,這個警告是多余的,因為 end 函數會消耗 self。結構體序列化器的所有權會轉移到 end 函數中,在那里它會被釋放且無法再次被使用。在 end 之后,嘗試調用序列化器的方法會引發編譯時錯誤:“借用了已移動的值”。這一特性在編譯時就消除了一整類的開發錯誤,從而節省了原本必須進行的大量的錯誤處理工作。

通過 Mutex 實現真正的受保護訪問

另一個強有力的例子涉及通過互斥鎖(mutex)對共享數據訪問進行建模。傳統方法將受保護的數據和互斥鎖視為互相獨立的實體,依賴開發者的自律,需要他們在訪問數據前先鎖定互斥鎖。Rust 則采取了一種更穩健的方法。

基于所有權的保護

Mutex::new 方法按值傳遞數據,這意味著數據會被移入互斥鎖中。數據的所有權發生轉移,在互斥鎖外部將無法再訪問該數據。加鎖成功時,它會返回一個 MutexGuard,其生命周期與互斥鎖本身綁定;該鎖守衛的生命周期不會超過互斥鎖。

pub struct MutexGuard<'a, T: ?Sized + 'a> {}

訪問受保護的數據需要解引用鎖守衛,該操作會返回一個引用。其生命周期與鎖守衛的生命周期綁定,而鎖守衛的生命周期又與互斥鎖的生命周期綁定。當 MutexGuard 被釋放時,它會解鎖互斥鎖。

drop(guard);

這些特性相結合可以提供一個強有力的保證:在移除鎖守衛之后,對受保護數據的引用將不復存在;而且,由于移除鎖守衛會解鎖互斥鎖,所以在未持有鎖的情況下,將無法持有或使用對受保護數據的引用。

根據我處理重度多線程代碼的經驗,我見過許多這樣的情況:開發者無意中持有引用,在代碼中傳遞這些引用,隨后卻將其遺忘,然后解鎖互斥鎖,從而引發難以察覺的并發錯誤。Rust 的設計方法徹底消除了這類錯誤。

泛型:強大的占位符

Rust 中的泛型是“占位符”,不同的具體類型可以重復使用其定義。Option 類型和類似 Vec 這樣的集合都體現了這種模式,即只需實現一次邏輯,就可以用于任何類型。泛型會在編譯時通過單態化進行替換,針對每種類型特化生成特定的代碼。這種代碼消除了運行時開銷,使得泛型代碼與為具體類型編寫的代碼一樣快。

泛型可以通過 trait(定義必需的行為)和生命周期進行約束,從而支持復雜的類型級編程。

結合泛型與類型安全的狀態機

雖然枚舉提供了一種直觀的狀態建模方式,但在某些情況下,另一種模式會更實用,例如“類型狀態”模式,它能在編譯時將狀態信息編碼到類型系統中。

將狀態編碼為類型

考慮三種表示機器人狀態類型:Uninit(空的、大小為零的標記類型)、Init(包含位置數據)和 ExecutingJob(包含位置和任務數據)。這些類型與之前的枚舉變體相對應,不過現在是作為獨立的類型存在。

一個針對狀態 S 的 Robot 類型的泛型,既能嵌入特定于狀態的數據,又能保留機器人名稱等通用數據。實現塊可以針對泛型機器人(用于訪問名稱等與狀態無關的功能),也可以針對特定的類型,例如 Robot 。

}

類型安全的狀態轉換

狀態轉換在具體類型上表現為方法實現。Robot 上的 init 函數獲取 self 的所有權,接收位置數據,并返回 Robot 。同樣,Robot 可以轉換為 Robot 。

位置訪問僅可針對保證位置可用的狀態 Robot 和 Robot 實現。若在 Robot 上調用 position() 方法,將導致編譯時錯誤,并顯示一條提示信息,說明哪些類型支持該方法。通過這種方式進行狀態建模,在訪問狀態相關的數據時就無需運行時檢查和錯誤處理了。

類型安全的構建器

這種模式可以優雅地擴展到構建器上。以需要初始位置和地圖數據的 RobotSimulationBuilder 為例。使用 NoPosition、PositionSet、NoMap 和 MapSet 等標記類型作為泛型參數,可以實現構建協議的編譯時強制檢查。

}

set_position 方法將 RobotSimulationBuilder 轉換為 RobotSimulationBuilder 。在這個類型上,set_position 方法沒有實現,因此無法再次調用。build 方法僅存在于完全配置好的類型上。

這種設計避免了耗時的重復操作,強制執行了必需的配置,并消除了因構建器配置不完整而導致的運行時錯誤,所有這些驗證都是在編譯時完成的。

健壯性并不一定難以實現

要實現讓人充滿信心的軟件仍然是一項富有挑戰性的問題,需要嚴格的流程和昂貴的工具。然而,從實踐角度來看,大多數開發人員并非在構建安全關鍵型應用程序,而只是在開發那些因用戶依賴而必須可靠的應用程序。

Rust 通過其類型系統實現了其他語言難以企及的健壯性。所有權系統、借用規則、生命周期和泛型相互配合,能夠在編譯時而非運行時捕獲整類的錯誤。

小 結

我認為,Rust 所提供的價值主張遠不止于其廣為人知的內存安全特性。通過所有權、借用、生命周期以及強大的泛型系統,開發者能夠將不變量、協議和約束直接編碼到類型系統中。在其他語言中可能導致運行時錯誤或隱蔽漏洞的問題,在 Rust 中會直接轉化為編譯時錯誤,從而使問題代碼無法部署到生產環境。

機器人和自主系統示例展示了這些技術的實際應用:防止對獨有資源的雙重使用,在機器人斷開連接時自動釋放物理區域,確保正確遵循序列化協議,以及保證共享數據的線程安全訪問。

盡管學習曲線確實比較陡峭,但堅持下來的開發者會發現,這門語言從根本上改變了他們與正確性之間的關系。健壯性并不一定難以實現。根據我的經驗,Rust 證明了一個設計良好的類型系統能夠捕獲僅靠測試難以發現的錯誤,從而使那些無力承擔形式驗證、卻因用戶依賴而必須確保正確運行的項目,也能更輕松地開發出可靠的軟件。

https://www.infoq.com/articles/practical-robustness-going-beyond-memory-safety-rust/

聲明:本文為 InfoQ 翻譯,未經許可禁止轉載。

特別聲明:以上內容(如有圖片或視頻亦包括在內)為自媒體平臺“網易號”用戶上傳并發布,本平臺僅提供信息存儲服務。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相關推薦
熱點推薦
80W全能卷王,輕薄本也能玩3A!惠普星Book Pro 16深度測評

80W全能卷王,輕薄本也能玩3A!惠普星Book Pro 16深度測評

電腦報
2026-04-20 16:51:12
克媒:莫德里奇顴骨骨折賽季報銷,預計將戴面具出戰世界杯

克媒:莫德里奇顴骨骨折賽季報銷,預計將戴面具出戰世界杯

懂球帝
2026-04-27 22:00:51
羅永浩連發6個問句怒懟!俞敏洪反思“東方甄選多位主播離職”,有網友說“任何一個人遭遇羅永浩、董宇輝這種忘恩負義的小人都夠嗆”

羅永浩連發6個問句怒懟!俞敏洪反思“東方甄選多位主播離職”,有網友說“任何一個人遭遇羅永浩、董宇輝這種忘恩負義的小人都夠嗆”

魯中晨報
2026-04-27 17:56:12
大五座SUV市場都在演戲!嵐圖泰山X8,撕碎所有偽大五座

大五座SUV市場都在演戲!嵐圖泰山X8,撕碎所有偽大五座

科技每日推送
2026-04-23 18:25:22
車企為什么開始把新車首發押在京東上?

車企為什么開始把新車首發押在京東上?

豹變
2026-04-25 08:00:03
匈牙利一夜變天:馬扎爾開啟全面清算!歐爾班往哪走?

匈牙利一夜變天:馬扎爾開啟全面清算!歐爾班往哪走?

大江看潮
2026-04-27 09:05:55
江淮汽車:目前尊界S800面向國內銷售

江淮汽車:目前尊界S800面向國內銷售

每日經濟新聞
2026-04-27 16:27:20
演員陸毅嘴唇發紫,被網友提醒去醫院體檢;陸毅聽勸后回應:心臟沒問題,體重卻超標了,身高1米8,體重180斤,“我是方的”

演員陸毅嘴唇發紫,被網友提醒去醫院體檢;陸毅聽勸后回應:心臟沒問題,體重卻超標了,身高1米8,體重180斤,“我是方的”

浙江之聲
2026-04-27 13:09:03
吉林伊通驚現200萬豪華住宅式墓地,獨門獨戶地上地下兩層

吉林伊通驚現200萬豪華住宅式墓地,獨門獨戶地上地下兩層

大象新聞
2026-04-27 13:41:32
克宮發出最后通牒要求烏作出“痛苦決定”,德國炮彈產能超越美國

克宮發出最后通牒要求烏作出“痛苦決定”,德國炮彈產能超越美國

史政先鋒
2026-04-27 18:27:59
拆解健合:一季度數據里的抗周期基因

拆解健合:一季度數據里的抗周期基因

一點財經
2026-04-27 18:31:37
這才是提高數學成績最好的方法!(建議永久收藏)

這才是提高數學成績最好的方法。ńㄗh永久收藏)

戶外阿毽
2026-04-27 09:46:34
這位派出所長,你瘋了嗎?

這位派出所長,你瘋了嗎?

新海言
2026-04-27 11:01:05
余承東在華為權力排名

余承東在華為權力排名

生活新鮮市
2026-04-27 18:30:53
32歲男子:一周多達9次,不幸猝死,妻子:多次勸說,他就是不聽

32歲男子:一周多達9次,不幸猝死,妻子:多次勸說,他就是不聽

川渝視覺
2026-04-27 22:04:50
開源模型橫掃21個科學任務!寬德Will聯手斯坦福清北,試錯變武器

開源模型橫掃21個科學任務!寬德Will聯手斯坦福清北,試錯變武器

機器之心Pro
2026-04-26 13:19:19
自導自演白宮記協晚宴槍擊事件?特朗普回應

自導自演白宮記協晚宴槍擊事件?特朗普回應

極目新聞
2026-04-27 19:31:28
我敢打賭99%的男人會選白衣服女孩做老婆,看腿型就知道

我敢打賭99%的男人會選白衣服女孩做老婆,看腿型就知道

朗威談星座
2026-04-26 10:47:21
隨著吳宜澤13-11勝塞爾比,火箭12-13,世錦賽8強對陣出爐附賽程

隨著吳宜澤13-11勝塞爾比,火箭12-13,世錦賽8強對陣出爐附賽程

小火箭愛體育
2026-04-28 00:09:23
上海地鐵32歲女子與66歲老太互毆后續:央媒發聲,拘留只是開始!

上海地鐵32歲女子與66歲老太互毆后續:央媒發聲,拘留只是開始!

青梅侃史啊
2026-04-27 11:38:30
2026-04-28 02:47:00
InfoQ incentive-icons
InfoQ
有內容的技術社區媒體
12309文章數 51863關注度
往期回顧 全部

科技要聞

DeepSeek V4上線三天,第一批實測出來了

頭條要聞

坐在特朗普身邊親歷槍擊案的女記者 身份非常不一般

頭條要聞

坐在特朗普身邊親歷槍擊案的女記者 身份非常不一般

體育要聞

人類馬拉松"破二"新紀元,一場跑鞋軍備競賽

娛樂要聞

黃楊鈿甜為“耳環風波”出鏡道歉:謠言已澄清

財經要聞

Meta 140億收購Manus遭中國發改委否決

汽車要聞

不那么小眾也可以 smart的路會越走越寬

態度原創

健康
游戲
家居
時尚
軍事航空

干細胞如何讓燒燙傷皮膚"再生"?

《AC黑旗》重制版新增專屬劇情!原版編劇親自執筆

家居要聞

江景風格 流動的秩序

絲巾的10種系法,愛美的女人必看

軍事要聞

伊朗外長折返伊斯蘭堡內情披露

無障礙瀏覽 進入關懷版