banner
davirain

davirain

twitter
github
知乎
twitter

Rust no-std 工程實踐

改寫 std 的庫為支持 no_std 的庫及寫出一個支持 std 和 no_std 庫的經驗談 github repo: https://github.com/DaviRain-Su/rust-no-std-source

簡介#

首先介紹 std 和 no_std 的區別,然後介紹使用 no_std 庫的方式,由於支持 no_std 的特性有兩種不同的方式,因此使用 no_std 庫也有兩種方式。其次,驗證一個庫是否支持 no_std 特性的驗證方式,如何改寫一個 std 的庫為同時支持 std 和 no_std 的特性方法。具體的如何寫一個支持 std 和 no_std 的庫。一些在 std 和 no_std 下都可以使用的 primitive 的倉庫和相關的資源和文章。

目錄#

  • std 和 no_std 的區別
  • Rust 中使用 no_std 的兩種方式
  • 驗證一個庫是否支持 no_std 特性的驗證方式
  • 具體的寫一個支持 std 和 no_std 的庫
  • 一些 no_std 和 std 可以使用 primitive 類型的倉庫和相關資源的文章

std 和 no_std 的區別#

核心庫#

Rust 語言的語法由核心庫和標準庫共同提供。 其中 Rust 核心庫是標準庫的基礎。核心庫中定義的是 Rust 語言的核心,不依賴於操作系統和網絡等相關的庫,甚至不知道堆分配,也不提供並發和 I/O

可以通過在模塊頂部引入 #![no_std] 來使用核心庫。核心庫和標準庫的功能有一些重複,包括如下部分:

  • 基礎的 trait,如 Copy、Debug、Display、Option 等。
  • 基本原始類型,如 bool、char、i8/u8、i16/u16、i32/u32、i64/u64、isize/usize、f32/f64、str、array、slice、tuple、pointer 等。
  • 常用功能型數據類型,滿足常見的功能性需求,如 String、Vec、HashMap、Rc、Arc、Box 等。
  • 常用的宏定義,如 println!、assert!、panic!、vec!等。做嵌入式應用開發的時候,核心庫是必需的。

標準庫#

Rust 標準庫提供應用程序開發所需要的基礎和跨平台支持。標準庫包含的內容大概如下:

  • 與核心庫一樣的基本 trait、原始數據類型、功能型數據類型和常用宏等,以及與核心庫幾乎完全一致的 API。
  • 並發、I/O 和運行時。例如線程模塊、用於消息傳遞的通道類型、Sync trait 等並發模塊,文件、TCP、UDP、管道、套接字等常見 I/O。
  • 平台抽象。os 模塊提供了許多與操作環境交互的基本功能,包括程序參數、環境變量和目錄導航;路徑模塊封裝了處理文件路徑的平台特定規則。
  • 底層操作接口,比如 std::mem、std::ptr、std::intrinsics 等,操作內存、指針、調用編譯器固有函數。
  • 可選和錯誤處理類型 Option 和 Result,以及各種迭代器等。

還有一些解釋,#![no_std] 是一個 crate level 級別的屬性,表示 core crate 將鏈接到 core crate 而不是 std crate。

下面是 std crate 和 core crate 的解釋,其實這裡也就解釋了標準庫與和核心庫之間的區別。當然也內在的包括了 std 與 no_std 之間的區別。

首先是,std crate 是 Rust 的標準庫。它包含的功能假定程序將在操作系統上運行,而不是直接在裸系統上運行。std 還假定操作系統是一個通用的操作系統,就像人們在服務器和桌面上看到的那樣。出於這個原因,std 為通常在這類操作系統中發現的功能提供了一個標準的 API: 线程、文件、套接字、文件系統、進程等等。

然後是,core crate 是 std crate 的一個子集,對程序運行的系統不做任何假設。因此它提供了基於語言的 API,如浮點,字符串和切片,以及暴露處理器特性的 API,如原子操作和 SIMD 指令。然而,它缺乏涉及堆內存分配和 I/O 的任何 API。

對於一個應用程序來說,std 所做的不僅僅是提供一種訪問操作系統抽象的方式,std 還負責涉及堆棧溢出保護,處理命令行參數,以及在程序的主函數被調用之前生成主線程。一個#![no_std]應用程序缺乏所有這些標準的運行時,所以它必須初始化自己的運行時,如果需要的話。

由於這些特性,#![no_std]應用程序可以是第一個或者唯一在系統上運行的代碼。

Rust 中 no_std 的一些使用方法#

主要具體介紹第二種方式的使用 no_std

具體如何使用,參見寫一個 no_std 的庫的第二種使用方式。

也可參考,實例:serde no-std 的使用規範

驗證一個庫是否支持 no_std 的驗證方式#

cargo check --target wasm32-unknown-unknown

但是 wasm 環境不一定就是 no_std,或者別的編譯目標也可以,也就是裸露的編譯目標環境不帶有任何系統的環境。

參考文檔: 使用 Rust 編寫操作系統(一):獨立式可執行程序

具體的寫一個 no_std 的庫#

創建一個 no_std 庫的第一種方式(使用#![no_std])

使用#![no_std]的話,默認的就是這個庫是在 no_std 環境下的,然而又因為 no_std 下的庫一般來說都是核心庫,而核心庫又是標準庫的子集,所以聲明 #![no_std] 寫出來的庫,也可以在 std(標準庫環境)下使用。

  1. 創建一個倉庫
  2. 使用 #![no_std] 將這個倉庫中的函數能支持在 no_std 和 std 下使用
  3. 開始添加一個函數編譯報錯 commit 1
  4. 修復錯誤 commit 2

創建 no_std 庫的第二種方式(使用 #![cfg_attr (not (features = "std"), no_std)] )#

  1. 創建一個倉庫
  2. 使用#![cfg_attr(not(feature = "std"), no_std)]
  3. 添加的一些函數和測試

使一些不能在 no_std 環境下運行的倉庫也能在 no_std 下支持

首先,要驗證這個庫能不能支持 no_std 的環境(見,驗證一個庫是否支持 no_std 的驗證方式)。

找出這個庫依賴的庫支持 no_std 的方式,如果使用的是#![no_std] 那麼這個庫本身就是可以在 std 和 no_std 下同時的運行。

如果使用的是#![cfg_attr(not(features = "std"), no_std)], 就需要打開default-features = false, 進行配置。

最後可能需要做一些標準庫的替換,使其能在 no_std 和 std 同時編譯成功,一些可以使用的類型庫有 sp-std (這個庫僅僅封裝了一部分的類型,例如有些類型是沒有的,string,File, IO) 當然,IO,File,這些標準庫在核心庫當中是沒有的。還有 rust 本身的 alloc, core 這些都是屬於核心庫的。也是在 no_std 環境下支持的。

具體的使用案例:

相關的 Pr, 使 ics23 支持 no_std

有些代碼也在 no_std 寫測試很難。因為這裡做了編譯選擇處理

一些 no_std 和 std 可以使用的 primitive 類型的倉庫#

引用及資源#

結論#

參照 serder 的使用以及一些論壇的討論,推薦使用#![cfg_attr(not(feature = "std"), no_std ))]來同時支持 std 和 no_std.

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。