自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

在 WASM WASI 上運行 Rust 的九條規(guī)則,你知道哪條?

開發(fā) 前端
如果 WASM+WASI 在 2008 年就存在,我們就無需創(chuàng)建 Docker。這就是它如此重要的原因。服務器上的 Webassembly 是計算的未來。標準化的系統(tǒng)接口是缺失的一環(huán)。讓我們希望 WASI 能勝任這項任務。

在受限環(huán)境中運行 Rust 會帶來挑戰(zhàn)。你的代碼可能無法訪問完整的操作系統(tǒng),例如 Linux、Windows 或 macOS。你可能對文件、網(wǎng)絡、時間、隨機數(shù)甚至內(nèi)存的訪問權限有限(或根本沒有)。我們將探索解決方法和解決方案。

本文的第一部分重點介紹在 “WASM WASI” 上運行代碼,這是一種類似容器的環(huán)境。我們將看到 WASM WASI 本身可能(也可能不)有用。但是,它作為在瀏覽器或嵌入式系統(tǒng)中運行 Rust 的第一步很有價值。

將代碼移植到 WASM WASI 上需要許多步驟和選擇。瀏覽這些選擇可能很耗時。錯過一步會導致失敗。我們將通過提供九條規(guī)則來減少這種復雜性,我們將在后面詳細探討:

規(guī)則 1:準備好失望:WASM WASI 很容易,但 - 現(xiàn)在 - 基本上沒用 - 除了作為墊腳石。

2019 年,Docker 聯(lián)合創(chuàng)始人 Solomon Hykes 發(fā)布了一條推文[1]:

如果 WASM+WASI 在 2008 年就存在,我們就無需創(chuàng)建 Docker。這就是它如此重要的原因。服務器上的 Webassembly 是計算的未來。標準化的系統(tǒng)接口是缺失的一環(huán)。讓我們希望 WASI 能勝任這項任務。

如今,如果你關注科技新聞,你就會看到像這樣的樂觀標題:

如果 WASM WASI 真正準備就緒并有用,每個人都會使用它。我們不斷看到這些標題的事實表明它還沒有準備好。換句話說,如果 WASM WASI 真的準備好了,他們就不需要不斷強調它已經(jīng)準備好了。

截至 WASI 預覽版 1,現(xiàn)狀如下:你可以訪問一些文件操作、環(huán)境變量,并可以訪問時間和隨機數(shù)生成。但是,不支持網(wǎng)絡功能。

WASM WASI 可能 對某些 AWS Lambda 風格的 Web 服務有用,但即使那也還不確定。因為,與 WASM WASI 相比,你難道不更愿意將你的 Rust 代碼本地編譯并以一半的成本運行兩倍的速度嗎?

也許 WASM WASI 對插件和擴展有用。在基因組學領域,我有一個用于 Python 的 Rust 擴展,我為 25 種不同的組合編譯它(5 個版本的 Python 跨 5 個操作系統(tǒng)目標)。即使這樣,我也沒有涵蓋所有可能的操作系統(tǒng)和芯片系列。我能用 WASM WASI 替換這些操作系統(tǒng)目標嗎?不能,它會太慢。我能將 WASM WASI 作為一個第六個“萬能”目標添加進去嗎?也許可以,但如果我真的需要可移植性,我已經(jīng)被要求支持 Python,應該直接使用 Python。

那么,WASM WASI 到底有什么用?目前,它的主要價值在于它是將代碼運行在瀏覽器或嵌入式系統(tǒng)中的第一步。

規(guī)則 2:了解 Rust 目標。

在規(guī)則 1 中,我順便提到了“操作系統(tǒng)目標”。讓我們更深入地了解 Rust 目標 - 這不僅對于 WASM WASI 來說是必要的信息,而且對于一般的 Rust 開發(fā)也是如此。

在我的 Windows 機器上,我可以編譯一個 Rust 項目以在 Linux 或 macOS 上運行。類似地,從 Linux 機器上,我可以編譯一個 Rust 項目以針對 Windows 或 macOS。以下是我用于將 Linux 目標添加到 Windows 機器并檢查它的命令:

rustup target add x86_64-unknown-linux-gnu
cargo check --target x86_64-unknown-linux-gnu

旁白:雖然 cargo check 驗證代碼是否可以編譯,但構建一個功能齊全的可執(zhí)行文件需要額外的工具。要從 Windows 交叉編譯到 Linux (GNU),你還需要安裝 Linux GNU C/C++ 編譯器和相應的工具鏈。這可能很棘手。幸運的是,對于我們關心的 WASM 目標,所需的工具鏈很容易安裝。

要查看 Rust 支持的所有目標,請使用以下命令:

rustc --print target-list

它將列出超過 200 個目標,包括 x86_64-unknown-linux-gnu、wasm32-wasip1 和 wasm32-unknown-unknown。

目標名稱包含最多四個部分:CPU 系列、供應商、操作系統(tǒng)和環(huán)境(例如,GNU 與 LVMM):

目標名稱部分 - 來自作者的圖片

現(xiàn)在我們對目標有所了解,讓我們繼續(xù)安裝我們需要的 WASM WASI 目標。

規(guī)則 3:安裝 wasm32-wasip1 目標和 WASMTIME,然后創(chuàng)建“Hello, WebAssembly!”。

要將我們的 Rust 代碼在瀏覽器之外的 WASM 上運行,我們需要將目標設置為 wasm32-wasip1(使用 WASI 預覽版 1 的 32 位 WebAssembly)。我們還將安裝 WASMTIME,這是一個允許我們在瀏覽器之外使用 WASI 運行 WebAssembly 模塊的運行時。

rustup target add wasm32-wasip1
cargo install wasmtime-cli

為了測試我們的設置,讓我們使用 cargo new 創(chuàng)建一個新的“Hello, WebAssembly!” Rust 項目。這將初始化一個新的 Rust 包:

cargo new hello_wasi
cd hello_wasi

編輯 src/main.rs 使其內(nèi)容如下:

fn main() {
    #[cfg(not(target_arch = "wasm32"))]
    println!("Hello, world!");
    #[cfg(target_arch = "wasm32")]
    println!("Hello, WebAssembly!");
}

旁白:我們將在規(guī)則 4 中更深入地了解 #[cfg(...)] 屬性,該屬性允許條件編譯。

現(xiàn)在,使用 cargo run 運行項目,你應該看到 Hello, world! 打印到控制臺上。

接下來,創(chuàng)建一個 .cargo/config.toml 文件,該文件指定 Rust 在針對 WASM WASI 時應該如何運行和測試項目。

[target.wasm32-wasip1]
runner = "wasmtime run --dir ."

旁白:這個 .cargo/config.toml 文件與主 Cargo.toml 文件不同,后者定義了你的項目的依賴項和元數(shù)據(jù)。

現(xiàn)在,如果你輸入:

cargo run --target wasm32-wasip1

你應該看到 Hello, WebAssembly!。恭喜!你剛剛成功地在類似容器的 WASM WASI 環(huán)境中運行了一些 Rust 代碼。

規(guī)則 4:了解條件編譯。

現(xiàn)在,讓我們研究一下 #[cfg(...)] - 這是在 Rust 中條件編譯代碼的重要工具。在規(guī)則 3 中,我們看到了:

fn main() {
    #[cfg(not(target_arch = "wasm32"))]
    println!("Hello, world!");
    #[cfg(target_arch = "wasm32")]
    println!("Hello, WebAssembly!");
}

#[cfg(...)] 行告訴 Rust 編譯器根據(jù)特定條件包含或排除某些代碼項。一個“代碼項”指的是代碼單元,例如函數(shù)、語句或表達式。

使用 #[cfg(…)] 行,你可以條件編譯你的代碼。換句話說,你可以為不同的情況創(chuàng)建代碼的不同版本。例如,在為 wasm32 目標編譯時,編譯器會忽略 #[cfg(not(target_arch = "wasm32"))] 塊,只包含以下內(nèi)容:

fn main() {
    println!("Hello, WebAssembly!");
}

你通過表達式指定條件,例如 target_arch = "wasm32"。支持的鍵包括 target_os 和 target_arch。有關支持的鍵的完整列表,請參閱 Rust 參考手冊 完整列表[2]。你還可以使用 Cargo 功能創(chuàng)建表達式,我們將在規(guī)則 6 中學習。

你可以使用邏輯運算符 not、any 和 all 來組合表達式。Rust 的條件編譯不使用傳統(tǒng)的 if...then...else 語句。相反,你必須使用 #[cfg(...)] 及其否定來處理不同的情況:

#[cfg(not(target_arch = "wasm32"))]
...
#[cfg(target_arch = "wasm32")]
...

要條件編譯整個文件,請將 #![cfg(...)] 放置在文件的頂部。(注意“!”)。當一個文件只與特定目標或配置相關時,這很有用。

你也可以在 Cargo.toml 中使用 cfg 表達式來條件包含依賴項。這允許你根據(jù)不同的目標定制依賴項。例如,這表示“當不針對 wasm32 時,依賴于具有 Rayon 的 Criterion”。

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
criterion = { version = "0.5.1", features = ["rayon"] }

規(guī)則 5:運行常規(guī)測試,但使用 WASM WASI 目標。

現(xiàn)在,讓我們嘗試在 WASM WASI 上運行 你的 項目。如規(guī)則 3 中所述,為你的項目創(chuàng)建一個 .cargo/config.toml 文件。它告訴 Cargo 如何在 WASM WASI 上運行和測試你的項目。

[target.wasm32-wasip1]
runner = "wasmtime run --dir ."

接下來,你的項目 - 就像所有好的代碼一樣 - 應該已經(jīng)包含測試[3]。我的 range-set-blaze 項目包含以下示例測試:

#[test]
fn insert_255u8() {
    let range_set_blaze = RangeSetBlaze::<u8>::from_iter([255]);
    assert!(range_set_blaze.to_string() == "255..=255");
}

現(xiàn)在,讓我們嘗試在 WASM WASI 上運行你的項目的測試。使用以下命令:

cargo test --target wasm32-wasip1

如果這能正常工作,你可能就完成了 - 但它可能不會正常工作。當我在 range-set-blaze 上嘗試這個命令時,我得到了一條錯誤消息,抱怨在 WASM 上使用 Rayon。

error: Rayon cannot be used when targeting wasi32. Try disabling default features.
  --> C:\Users\carlk\.cargo\registry\src\index.crates.io-6f17d22bba15001f\criterion-0.5.1\src\lib.rs:31:1
   |
31 | compile_error!("Rayon cannot be used when targeting wasi32. Try disabling default features.");

要修復此錯誤,我們首先需要了解 Cargo 功能。

規(guī)則 6:了解 Cargo 功能。

為了解決像規(guī)則 5 中的 Rayon 錯誤這樣的問題,了解 Cargo 功能如何工作非常重要。

在 Cargo.toml 中,一個可選的 [features] 部分允許你根據(jù)啟用的功能或禁用的功能來定義項目的不同配置或版本。例如,以下是 Criterion 基準測試項目 的 Cargo.toml 文件的簡化部分:

[features]
default = ["rayon", "plotters", "cargo_bench_support"]
rayon = ["dep:rayon"]
plotters = ["dep:plotters"]
html_reports = []
cargo_bench_support = []
[dependencies]
#...
# 可選依賴項
rayon = { version = "1.3", optional = true }
plotters = { version = "^0.3.1", optional = true, default-features = false, features = [
    "svg_backend",
    "area_series",
    "line_series",
] }

這定義了四個 Cargo 功能:rayon、plotters、html_reports 和 cargo_bench_support。由于每個功能都可以包含或排除,因此這四個功能創(chuàng)建了項目的 16 種可能的配置。還要注意特殊的默認 Cargo 功能。

一個 Cargo 功能可以包含其他 Cargo 功能。在上面的示例中,特殊的 default Cargo 功能包含了另外三個 Cargo 功能 - rayon、plotters 和 cargo_bench_support。

一個 Cargo 功能可以包含一個依賴項。上面的 rayon Cargo 功能包含 rayon 箱子作為依賴包。

此外,依賴包可能擁有自己的 Cargo 功能。例如,上面的 plotters Cargo 功能包含 plotters 依賴包,并啟用了以下 Cargo 功能:svg_backend、area_series 和 line_series。

你可以在運行 cargo check、cargo build、cargo run 或 cargo test 時指定要啟用或禁用的 Cargo 功能。例如,如果你正在使用 Criterion 項目并只想檢查 html_reports 功能,而不使用任何默認功能,你可以運行:

cargo check --no-default-features --features html_reports

此命令告訴 Cargo 不要默認包含任何 Cargo 功能,而是專門啟用 html_reports Cargo 功能。

在你的 Rust 代碼中,你可以根據(jù)啟用的 Cargo 功能包含/排除代碼項。語法使用 #cfg(…),如規(guī)則 4 所示:

#[cfg(feature = "html_reports")]
SOME_CODE_ITEM

了解了 Cargo 功能之后,我們現(xiàn)在可以嘗試修復在 WASM WASI 上運行測試時遇到的 Rayon 錯誤。

規(guī)則 7:更改你能更改的東西:通過選擇 Cargo 功能解決依賴問題,64 位/32 位問題。

當我們嘗試運行 cargo test --target wasm32-wasip1 時,錯誤消息的一部分指出:Criterion ... Rayon cannot be used when targeting wasi32. Try disabling default features. 這表明我們應該在針對 WASM WASI 時禁用 Criterion 的 rayon Cargo 功能。

為此,我們需要在 Cargo.toml 中進行兩個更改。首先,我們需要在 [dev-dependencies] 部分禁用 Criterion 的 rayon 功能。因此,這個起始配置:

[dev-dependencies]
criterion = { version = "0.5.1", features = ["html_reports"] }

變成了這個,我們顯式地關閉 Criterion 的默認功能,然后啟用除 rayon 之外的所有 Cargo 功能。

[dev-dependencies]
criterion = { version = "0.5.1", features = [
        "html_reports",
        "plotters",
        "cargo_bench_support"
      ],
      default-features = false }

接下來,為了確保 rayon 仍然用于非 WASM 目標,我們在 Cargo.toml 中添加了一個條件依賴項,如下所示:

[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
criterion = { version = "0.5.1", features = ["rayon"] }

一般來說,在針對 WASM WASI 時,你可能需要修改你的依賴項及其 Cargo 功能以確保兼容性。有時這個過程很簡單,但有時它可能很困難 - 甚至不可能,正如我們將在規(guī)則 8 中討論的那樣。

旁白:在本系列的下一篇文章中 - 關于瀏覽器中的 WASM - 我們將更深入地探討修復依賴項的策略。

再次運行測試后,我們越過了之前的錯誤,卻遇到了一個新的錯誤,這是一種進步!

#[test]
fn test_demo_i32_len() {
    assert_eq!(demo_i32_len(i32::MIN..=i32::MAX), u32::MAX as usize + 1);
                                                  ^^^^^^^^^^^^^^^^^^^^^ attempt to compute
`usize::MAX + 1_usize`, which would overflow
}

編譯器抱怨 u32::MAX as usize + 1 溢出了。在 64 位 Windows 上,該表達式不會溢出,因為 usize 與 u64 相同,并且可以容納 u32::MAX as usize + 1。但是,WASM 是一個 32 位環(huán)境,因此 usize 與 u32 相同,該表達式大了一個。

這里的解決方法是用 u64 替換 usize,確保表達式不會溢出。更一般地說,編譯器不會總是捕獲這些問題,因此審查你對 usize 和 isize 的使用非常重要。如果你指的是 Rust 數(shù)據(jù)結構的大小或索引,usize 是正確的。但是,如果你處理的值超過了 32 位限制,你應該使用 u64 或 i64。

旁白:在 32 位環(huán)境中,Rust 數(shù)組、Vec、BTreeSet 等只能容納最多 232?1=4,294,967,295 個元素。

因此,我們已經(jīng)解決了依賴問題并解決了 usize 溢出問題。但是,我們能修復所有問題嗎?不幸的是,答案是否定的。

規(guī)則 8:接受你無法更改所有東西:網(wǎng)絡、Tokio、Rayon 等。

WASM WASI 預覽版 1(當前版本)支持文件訪問(在指定目錄內(nèi))、讀取環(huán)境變量以及處理時間和隨機數(shù)。但是,與你可能從完整操作系統(tǒng)中期望的功能相比,它的功能有限。

如果你的項目需要訪問網(wǎng)絡、使用 Tokio 進行異步任務或使用 Rayon 進行多線程,不幸的是,這些功能在預覽版 1 中不受支持。

幸運的是,WASM WASI 預覽版 2 預計將改進這些限制,提供更多功能,包括對網(wǎng)絡和可能異步任務的更好支持。

規(guī)則 9:將 WASM WASI 添加到你的 CI(持續(xù)集成)測試中。

因此,你的測試在 WASM WASI 上通過了,你的項目也成功運行了。你完成了?還沒有。因為,正如我喜歡說的:

如果不在 CI 中,它就不存在。

持續(xù)集成 (CI) 是一個系統(tǒng),它可以在你每次更新代碼時自動運行你的測試,確保你的代碼能夠繼續(xù)按預期工作。通過將 WASM WASI 添加到你的 CI 中,你可以保證未來的更改不會破壞你的項目與 WASM WASI 目標的兼容性。

在我的情況下,我的項目托管在 GitHub 上,我使用 GitHub Actions 作為我的 CI 系統(tǒng)。以下是我添加到 .github/workflows/ci.yml 中的配置,用于在我的項目上測試 WASM WASI:

test_wasip1:
      name: Test WASI P1
      runs-on: ubuntu-latest
      steps:
        - name: Checkout
          uses: actions/checkout@v4
        - name: Set up Rust
          uses: dtolnay/rust-toolchain@master
          with:
            toolchain: stable
            targets: wasm32-wasip1
        - name: Install Wasmtime
          run: |
            curl https://wasmtime.dev/install.sh -sSf | bash
            echo "${HOME}/.wasmtime/bin" >> $GITHUB_PATH
        - name: Run WASI tests
          run: cargo test --verbose --target wasm32-wasip1

通過將 WASM WASI 集成到 CI 中,我可以放心地向我的項目添加新代碼。CI 將自動測試所有代碼在未來繼續(xù)支持 WASM WASI。

因此,這就是將你的 Rust 代碼移植到 WASM WASI 的九條規(guī)則。以下是我對移植到 WASM WASI 的感受:

不好之處:

  • 在 WASM WASI 上運行在今天幾乎沒有實用價值。但是,它有潛力在明天變得有用。
  • 在 Rust 中,有一句常見的說法:“如果它可以編譯,它就可以工作。”不幸的是,這并不總是適用于 WASM WASI。如果你使用了不支持的功能,比如網(wǎng)絡功能,編譯器將不會捕獲錯誤。相反,它將在運行時失敗。例如,這段代碼可以在 WASM WASI 上編譯和運行,但始終返回錯誤,因為不支持網(wǎng)絡功能。
use std::net::TcpStream;
fn main() {
    match TcpStream::connect("crates.io:80") {
        Ok(_) => println!("Successfully connected."),
        Err(e) => println!("Failed to connect: {e}"),
    }
}

好之處:

  • 在 WASM WASI 上運行是將代碼運行在瀏覽器和嵌入式系統(tǒng)中的一個很好的第一步。
  • 你可以在 WASM WASI 上運行 Rust 代碼,而無需移植到 no_std。(移植到 no_std 是本系列文章的第三部分的主題。)
  • 你可以在 WASM WASI 上運行標準的 Rust 測試,這使得驗證你的代碼變得很容易。
  • .cargo/config.toml 文件和 Rust 的 --target 選項使得在不同的目標上配置和運行你的代碼變得非常簡單 - 包括 WASM WASI。

參考資料

[1] 發(fā)布了一條推文: https://x.com/solomonstre/status/1111004913222324225

[2] 完整列表: https://doc.rust-lang.org/reference/conditional-compilation.html#set-configuration-options

[3] 你的項目 - 就像所有好的代碼一樣 - 應該已經(jīng)包含測試: https://doc.rust-lang.org/rust-by-example/testing.html

責任編輯:武曉燕 來源: Rust開發(fā)筆記
相關推薦

2024-01-05 09:08:48

代碼服務管理

2020-07-29 07:52:25

編程開發(fā)IT

2024-04-17 08:05:18

C#并發(fā)設計

2023-09-01 10:43:22

IT外包企業(yè)

2025-04-27 08:06:50

2022-01-04 05:51:03

C++Python開發(fā)

2019-07-09 13:42:12

數(shù)據(jù)備份云計算系統(tǒng)

2011-05-16 13:44:11

C++

2011-03-31 09:22:56

c++

2025-01-22 00:00:00

異常catch編程

2019-09-30 08:00:00

圖數(shù)據(jù)庫數(shù)據(jù)庫

2011-08-29 16:05:07

高性能SQL語句SQL Server

2011-03-24 12:32:15

數(shù)據(jù)庫性能優(yōu)化

2016-10-28 13:21:36

2015-12-31 10:00:41

Java日志記錄規(guī)則

2023-06-06 07:17:44

云變化管理策略

2022-02-07 11:24:08

云安全云計算

2023-11-06 15:04:07

Flutter開發(fā)技巧

2020-02-28 15:19:19

CIOCISO安全

2023-11-04 12:08:40

Flutter事件
點贊
收藏

51CTO技術棧公眾號