「物聯網」Rust系列3:Rust如何避免C和c++的陷阱

2021-01-07 網易

  這是關於我們在dwell如何在Rust中重寫物聯網平臺的系列文章的第3部分。

  第一部分:「物聯網」Rust系列1:用Rust重寫了物聯網平臺並成功

  第二部分:「物聯網」Rust系列(2):以火取光,C和C++的問題

  所以現在我已經徹底,也許不公平烤幾個設計缺陷的一種程式語言超過四十歲,經營著世界上大多數嵌入式設備,讓我們來談談如何鏽設計出這些問題,同時仍然保留了C和c++的部分,讓他們強大的和有用的語言。

  注意:具體來說,我將在這裡討論「安全」生鏽。你仍然可以使用unsafe關鍵字跳過護欄。但通常大多數代碼都不需要這樣做。

  代數數據類型

  「代數數據類型」是描述枚舉類型的一種奇特方式,枚舉類型是完全集成的,實際上是正常的和安全的,並且允許語言規範強制執行最佳實踐。在經典語言的現代版本中,它們是一個常見的特性,因為它們具有關於正確性的有用屬性。這是Scala、Kotlin和Swift等語言相對於Objective C和舊版本Java的優勢。代數類型有點像類固醇上的C枚舉:Rust枚舉可以包含數據欄位。它們也類似於C併集,因為它們只佔用最大欄位的空間(在大多數情況下,加上一個鑑別器)。但與union不同的是,您不會意外地將欄位的字節誤讀為錯誤的變體。

  因為這是一個有點抽象的概念,所以最好使用兩個通用類型來解釋它的實用程序,它們是核心語言的一部分,並且到處都在使用:Result和Option。

  Result是一種類型,既可以是成功值,也可以是錯誤值。C函數通常會返回一個可能為負的int值或一個可能為0的文件句柄,而Rust的做法不同。

  use std::fs::File;use std::io::prelude::*;fn open_with_header() -> Result { let mut file = File::create("foo.txt")?; file.write_all(b"Header line\n")?; Ok(file)}

  create的返回類型是一個包含文件句柄或錯誤的單數項。甚至在您可以使用文件句柄之前,您必須解包結果類型並對潛在的錯誤進行處理。在上面的例子中,?操作符在出現錯誤時從函數中提前返回。如果成功,包裝好的文件將被解包到File變量中,我們可以使用它。注意,write_all調用也會返回錯誤,我們必須處理它。同樣,這個例子使用了?操作符,因為作者想用一個早期的返回過濾該錯誤。我們可以很容易地列印一個錯誤消息並跳過文件操作,或者提供一個替代的默認值,甚至驚慌失措並立即停止程序。但我們不能就這樣無視它。

  fn frob_widget() -> Result<(), SomeErrorType> { ... }frob_widget(); // Compiler warningfrob_widget().unwrap(); // Halts with a stack trace on failure

  對於在正常情況下不返回任何內容的函數,代碼可以表示可能發生錯誤並必須處理。

  Option表示某物可能存在也可能不存在的情況。假設您要求一個鍵/值存儲(一個字典或一個映射,取決於您在哪裡學習這個概念)返回並刪除與鍵相關聯的值。如果鍵在存儲中,函數應該返回值。如果不是,函數應該返回沒有值的值。對於結果,你不能只是假設它存在然後使用它。這裡有一個例子:

  use std::collections::HashMap;let mut map = HashMap::new();map.insert(1, "a");assert_eq!(map.remove(&1), Some("a"));assert_eq!(map.remove(&1), None);

  在我們刪除字符串之後,它就不在映射中了,所以第二次嘗試刪除它將返回None。

  沒有神秘的指針

  Rust為了引用而放棄指針。通過圍繞引用的一組聰明的設計決策,safe Rust消除了普遍存在於C和c++程序中的「神秘指針」問題。

  默認情況下使用常量

  在C語言中,變量和函數參數默認情況下是可變的,const關鍵字用於限制可變性。在Rust中,情況恰恰相反:變量和函數參數默認情況下是const,您必須添加關鍵字來表示不是const。這有一個非常微妙的影響,即不鼓勵帶有副作用的代碼,並促進具有較少移動部件的編碼風格。如果您的代碼在不必要的時候使用mut關鍵字,編譯器會生成一個警告。

  構建和return-by-move(Build and return-by-move)

  將未初始化指針傳遞給函數來存儲結果是C中的常見做法,但這也是傳遞要在適當位置讀取和修改的結構體的標準方法。這造成了一些輸入和輸出的混合,並且允許在某些場景中使用有效數據編寫「輸出」指針,而在其他場景中不進行初始化。例如:

  /* Modifies an entity position and returns nonzero on error. */
/* Writes the Cartesian distance changed into distance */
/* if the object could be moved. */
int move(obj_t *obj, double *distance, const vec_t *v);

  在Rust中,規範的例子使意圖更加清晰,並防止指向未初始化的double對象的懸空指針:

  /// If successful, returns distance moved
fn move(&mut self, v: &Coordinates) -> Result { ... }

  References always point to something

  在Rust中,一個引用總是指向一個實際的t。就像c++引用一樣,一個Rust引用不能為空——在安全的Rust範圍內,不可能故意或無意地創建一個指向「null」或一個尚未創建的結構體的引用。如果只有一個對對象的引用,那麼也沒有辦法釋放對象。此外,還有另一個聰明的特性允許該語言提供更強的保證。

  Rust引用具有生存期。這是《Rust》真正獨特的地方,而且這個想法有最大的學習曲線。在編譯時,這種對語言的添加保證了沒有辦法從引用中釋放或移動對象——如果您試圖以一種可能危及這種保證的方式對潛在的引用對象做一些事情,程序將無法編譯。這種保證甚至跨線程也適用。永遠和免費使用問題說再見吧!

  不需要空指針

  在C/ c++中,你想要傳遞一個指向可選數據的指針,例如,一個可能指向或可能不指向某物的指針,用例是什麼呢?在這些語言中,您將傳遞一個指針參數,然後(希望如此)函數實現將在使用它之前檢查是否為空。在Rust中,選項<&T>是安全的選擇。Rust內部使用指針來表示它的引用類型,所以在那些指針值不是0的計算機上(例如Rust支持的架構),編譯器將優化選項<&T>的實現,以避免枚舉的任何大小懲罰。如果你真的對細節感興趣,你可以深入了解這個主題。

  總而言之:對於不需要動態調度的T,選項<&T>生成的機器碼與正確的null檢查的C指針相同。它是更安全。

  Slices, not pointers

  C中的數組只是帶有特殊語法的指針。如果API文檔不清楚,這可能會導致各種混亂。在Rust中,對單個對象的引用具有不同於複合類型的語法,因此這兩者不會意外混淆。

  對於複合類型,Rust為可變大小的數組(vec)、固定大小的數組和連續數據的「切片」提供了不同的類型。這些複合類型都天生知道它們的大小,並通過函數範式和命令式循環支持迭代。如果在Rust中使用數組索引訪問複合類型,則在運行時對訪問進行邊界檢查。這使得不可能無聲地溢出緩衝區。(你可以通過使用迭代器來完全避免這種檢查。)

  總之:《Rust》中的參考(references )是可以預測的

  當閱讀我自己或其他人的生鏽代碼時,引用的這些屬性使我作為程式設計師能夠更好地假設函數調用的兩端是什麼。如果我調用一個函數,它返回選擇<科技>函數是告訴我它會返回什麼,我必須明白參考使用壽命有限,而且指向不可變數據,我可以立即調用功能,甚至可以克隆對象,但我不能修改它。另一方面,選項<&'static T>的返回值表明,如果返回的引用存在,則保證在程序的整個執行過程中是有效的。如果一個函數接受String作為參數,這意味著該函數將使用該字符串而不返回它。我可以安全地將數組的一部分作為不可變片傳遞,而不必擔心緩衝區溢出,而且籤名使我確信函數不會嘗試修改或釋放內存。指針的所有功能和靈活性都是存在的,但未定義的行為是設計出來的。

  安全的轉換規則(Safer casting rules)

  在給定的平臺上,u64和usize在內存中可能有相同的表示,但實際上它們是需要顯式轉換的不同類型。在大多數情況下,這消除了64位的可移植性問題——顯式強制類型轉換在代碼審查中很顯眼,而不是潛伏在普通的數學表達式中間。這鼓勵每個人從一開始就使用正確的類型。如果有捨入錯誤,有一個明顯的地方開始調試。

  我不會撒謊說隱式類型轉換已經完全消失了。仍然有一些無聲的轉換可以發生在「引用到引用到T」(這通常會消除混亂的地方,只有一種明智的方式來做事情),但大多數情況下,很少有魔法發生。

  線程安全

  

  在類型系統中存在生存期,這使得編譯器可以防止您意外地使用引用做一些愚蠢的事情。如果試圖在線程之間將一個裸引用傳遞給一個堆分配或堆棧分配的變量,這是一個編譯錯誤,並且會提醒您將對象包裝在一個原子引用計數器(Arc)中,以防止使用後使用的可能性。如果至少有一個持有該引用的線程需要寫訪問,那麼這個對象需要被包裝在互斥鎖或RwLock中,以避免數據競爭。與其他一些語言不同的是,鎖完全包裝了原始對象,因此不可能在不獲得鎖的情況下意外地訪問它。

  如果您只是需要一個線程安全的隊列,可以使用內置的性能。創建一個mpsc,將接收端移動到另一個線程中,這樣就完成了。在工作中使用合適的工具很容易,而且很有效。

  如果所有這些聽起來都非常複雜,那是因為正確執行線程實際上是非常複雜的。如果您在c++中使用任何類型的共享狀態進行線程化工作,並且不是什麼天才,那麼您可能會犯至少一個微妙的錯誤。如果你在一個團隊中編寫一個多線程應用程式,你最好希望每個接觸代碼的人都能始終如一地遵循你所能想到的最嚴格的代碼指導方針——即使這樣也不能保證每個部分不會完全對齊。但在Rust中,當線程代碼編譯時,有強大的正確性保證。它保證在你的代碼和你接觸的所有其他生鏽的代碼中不存在數據競爭。Rust團隊稱這個概念為「無畏的並發」,經過幾十年的追蹤線程bug,我發現它令人難以置信的解放。

  您仍然可以在生鏽中編寫不可維護的代碼。您仍然可以編寫帶有bug和死鎖的代碼。但這種語言會溫和地引導您找到乾淨、易讀的解決方案。結果,很多精神包袱都消失了。

  在dwell,我們想為我們的智能公寓物聯網平臺建立一個可靠的嵌入式系統。我們希望它能被普通人維護,它需要快速,並且我們希望避免在初始實現中普遍存在的線程安全問題。我們選了拉斯特,這是正確的決定。

  在下一個系列中,我們將開始深入我們的實際實現的核心,並詳細介紹我們在哪些方面遇到了困難(並希望避免其他人做同樣的事情)。

  本文:http://jiagoushi.pro/node/1440

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

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.

相關焦點

  • VS Code 搭建 Rust 開發環境
    上一篇文章安裝和配置好了 Rust 環境後,我們是使用的是簡單的文本工具編寫 Hello World 入門代碼,但是為了提高我們的學習效率,下面安利大家 VS Code 搭建 Rust 開發環境,讓我們開始享受 IDE 帶來的便利。
  • Rust 入坑指南:鱗次櫛比|CSDN 博文精選
    在Rust入坑指南:常規套路(https://blog.csdn.net/K_Ohaha/article/details/102481562)一文中我們已經介紹了一些基本數據類型了,它們都存儲在棧中,今天我們重點介紹3種數據類型:string,vector和hash map。
  • Rust 1.0發布半年後社區湧現的項目集錦
    詳情請移步:http://www.redox-os.org/3. Coreutils - 準備全面替換gnu core utils的rust套件Redox實現的是作業系統內核、圖形界面、文件系統等基礎功能,而Coreutils就是實現全套GNU coreutils這套基本工具集了,相當於它是Redox之上的一層,可以配合Redox使用。
  • Rust 1.49.0 發布,增強對 ARM 架構的支持
    雖然 Rust 編譯器支持多平臺,但 Rust 團隊無法為所有目標平臺提供相同級別的支持,為了清楚地標記對每個平臺的支持級別,他們採用 3 個層級進行區分: Tier 3:Rust 編譯器對 Tier 3 級別平臺只提供技術性的初級支持 (technically support),並不檢查運行於這些平臺之上的代碼能否構建或通過測試,也不會提供任何預構建的二進位文件作為發布的一部分。
  • 2019 年 Python、Golang、Java、C++、Rust 該如何選擇?
    Python、Golang、Java、C++、Rust這幾門程式語言都是2019非常火熱的語言,這幾門語言各有特點各有優劣,因此很多初學者總是糾結於如何在這五種語言間選擇。
  • 「咖啡品種」蒙多諾沃MUNDO NOVO和帕卡馬拉PACAMARA介紹
    前言AK 培訓工作室以世界咖啡研究中心的阿拉比卡咖啡種類手冊為基礎準備開啟咖啡品種類課程,本文則是翻譯此手冊產生的系列文章。此品種植株非常高-之美洲是適應性強高產晚熟品種-其葉尖顏色是綠色或棕色 其和卡杜拉雜交產生新品種卡杜艾。帕卡馬拉(PACAMARA )Capable of producing exceptional cup quality.
  • 咖啡品種之T8667和庫斯卡特洛CUSCATLECO介紹
    前言AK 培訓工作室以世界咖啡研究中心的阿拉比卡咖啡種類手冊為基礎準備開啟咖啡品種類課程,本文則是翻譯此手冊產生的系列文章。Resistant to coffee leaf rust and some nematodes. 適應中等海拔抗咖啡葉銹病和一些線蟲品種。。此品種是薩爾瓦多咖啡研究基金會(PROCAFE)對T5296的系譜選擇品種。
  • 咖啡品種之坎特斯客CATISIC和哥斯大黎加95介紹
    前言AK 培訓工作室以世界咖啡研究中心的阿拉比卡咖啡種類手冊為基礎準備開啟咖啡品種類課程,本文則是翻譯此手冊產生的系列文章。哥斯大黎加95 (COSTA RICA 95)High yielding dwarf variety resistant to coffee leaf rust and coffee berry disease.
  • 5個必看貼士,如何避免訂airbnb中「陷阱」!
    要避免這些住宿「陷阱」,以下 5 點要緊記!預訂時要留意:1、屋主可信度Airbnb 要求房客、屋主均需以實名登記,提供身分證件、信用卡號碼、電話號碼、email 等人資料而核實身份。訂Airbnb 時,可以留意屋主是否「已驗證」帳號、入會年數、回覆率及評價等,如果屋主被列為「superhost」更好,遇上「假屋主」可就糟心了!
  • 最終,我們放棄了 GO,遷移至 Rust,特性使然
    在 Discord,我們看到了 Rust 在客戶端和服務端的成功。舉例來說,我們在客戶端使用它實現了 Go Live 的視頻編碼管道,在服務端,它則被用於 Elixir NIFs。最近,我們通過將服務的實現從 Go 切換到 Rust,極大地提升了該服務的性能。本文闡述了重新實現服務為何是有價值的、該過程是如何實現的以及由此帶來的性能提升。
  • 買房會遇到哪些購房陷阱?如何避免掉入陷阱
    如何避免掉入陷進!買房會遇到哪些購房陷阱?1、宣傳陷阱購買新房的時候,大家都是通過廣告宣傳去了解樓盤的,所以購房者遇到宣傳陷阱的機率很大。2、樣板房陷阱大多數的樓盤都是有設置樣板房的,購房者可不要以為樣板房就是自己購買的房子。刻意美化樣板房已是開發商銷售房屋的一種慣用方式,其實無論開發商如何宣傳,只有把宣傳內容都寫進合同裡才能得到保障。
  • 「小馬智行」宣布獲得2.67億美元C輪融資,印尼支付平臺「LinkAja...
    抑制腫瘤的抗藥機制,包括通過激活c-Met信號的逃逸機制,從而達到更好的靶向治療效果;3. 防止腫瘤細胞躲藏到骨髓等器官,避免抗腫瘤藥物找不到腫瘤細胞,從而加強抗癌藥物的作用。Apollomics開發的藥物APL-101是一種新型的口服且具有高度選擇性的小分子c-Met抑制劑,可靶向多種腫瘤c-Met失調通路。
  • 「咖啡品種」貝內西亞(VENECIA )和維拉薩奇(VILLA SARCHI)介紹
    前言AK 培訓工作室以世界咖啡研究中心的阿拉比卡咖啡種類手冊為基礎準備開啟咖啡品種類課程,本文則是翻譯此手冊產生的系列文章。貝內西亞(VENECIA) Very high susceptibility to coffee leaf rust. Well-adapted to rainy zones due to late harvest during dry season.非常容易感染葉銹病。
  • 2020智博會|「香港智慧城市展館」 展示香港企業 (物聯網、金融...
    香港智慧城市聯盟獲香港特別行政區政府工業貿易署「工商機構支援基金」(TSF) 撥款資助,將於12月3-5日在北京展覽館舉辦的第六屆中國智慧城市博覽會(簡稱智博會),設置「香港智慧城市展館」,展示香港26家公司與物聯網、金融科技、訊息安全範疇有關的產品或服務。
  • 「世說X語」更新:見識不一樣的泰國/出租房裡的怪事和陷阱
    「世說X語」更新:見識不一樣的泰國/出租房裡的怪事和陷阱 2020-03-31 19:24 來源:澎湃新聞·澎湃號·湃客
  • 3個「女」—3個「牛」—3個「毛」,知道如何讀音嗎—日語
    3個「女」—3個「牛」—3個「毛」,知道如何讀音嗎—日語3個「牛」組合在一起,如何讀音呢?↓會讀了嗎?↓會讀了嗎?↓會讀了嗎?意思是:很多的人或是物,集結在一起,擁擠不堪的樣子。那麼,3個「女」湊集在一起,如何讀音呢?↓會讀了嗎?↓會讀了嗎?↓會讀了嗎?やかましい、騒々しい、耳障りでうるさいといった意味ですね。「かしましい」は他にも、「喧しい」「囂しい」とも書きます。意思是:喧囂,吵鬧等。那麼,3個「毛」組合在一起,如何讀音呢?↓會讀了嗎?
  • 山系穿搭如何避免「用力過猛」?
    ISSUE #1111By Max#OUTDOOR #SALOMON #BEACHCOVER / KIDULTY「到底為什麼穿山系戶外ISSUE #1111By Max#OUTDOOR #SALOMON #BEACHCOVER / KIDULTY「到底為什麼穿山系戶外?」
  • 屌絲產品經理如何用馬斯克的「第一法則」進行日常工作?
    如果你實在太懶,那我就用自己太爛的英文翻譯一下吧:我認為依據「第一法則」來思考要比依據「類比法則」來思考重要。我們日常生活的思考方式是「類比法則」。在「類比法則」下,我們做一件事是因為別人已經這麼做。而「第一法則」要求你把事情簡化成基本模型,然後由此進行推導。他喵的到底在說些什麼?
  • 法國人就「吃」這一套!如何避開陷阱,買到正宗法蘭西美食?
    @歐棒巴黎原創文章,轉載請聯繫後臺一直以來,「法國製造」都以品質著稱,紅酒、香水、食品等品牌無不以「法國製造」而自豪,而遊客和法國本地人也會更偏向於挑選買得放心的「法國製造」產品。圖片來源:歐棒巴黎下面是一些在法國選擇本地美食時避開「陷阱」的小訣竅:🇫🇷訣竅之一 🔍不是有「法國」或者「FR」兩個字就是法國製造!
  • 36氪首發丨蜂窩物聯網晶片公司「智聯安」完成近億元A+輪融資,加速...
    36氪獲悉,蜂窩物聯網晶片公司「智聯安」已於近日完成近億元A+輪融資。本輪融資投資方為SIG海納亞洲創投基金,所募集資金將主要用於公司發展商業化並加大晶片測試及研發投入。「智聯安」全稱北京智聯安科技有限公司,成立於2013年9月,是一家物聯網應用領域晶片解決方案提供商,專業從事晶片設計,總部位於中國北京,在矽谷、武漢、合肥等多地設有子公司和技術研發中心。