在Web和嵌入式系统上运行Rust的九条规则
9 rules for running Rust on the Web and embedded systems
将 range-set-blaze
移植到 no_std 和 WASM 的实践经验

当您想要 C++ 的速度和 Python 的内存安全性时,我推荐使用 Rust。除此之外,使用 Rust,您可以构建超过 100,000 个软件库。此外,Rust 还提供了在传统计算机上运行代码的潜力,甚至在网页或机器人内部运行。
然而,“几乎可以运行在任何地方”也会带来一些复杂性。本文是给那些想要缓解这些复杂性的 Rust 程序员的。对于那些想要了解 Rust 的“几乎可以运行在任何地方”故事的人,本文也可能会有所帮助。
第一个复杂性:网页和机器人的嵌入式处理器不支持一般的文件 IO。如果您的项目主要涉及读写文件,那么它不适合在机器人、其他嵌入式处理器或网页上运行。
第二个复杂性:将代码移植到几乎可以运行在任何地方需要很多步骤和选择。在这些选择中导航可能会耗费时间。遗漏一个步骤可能导致失败。本文旨在通过提供以下九个规则来减少这第二个复杂性,稍后我们将详细探讨这些规则:
- 将 lib.rs 或 main.rs 标记为 no_std。
- 如果可能,使用内置的 “crate alloc”。
- 切换到 “no std” 依赖。
- 创建 std 和 alloc 特性,并将 std-only 函数设为可选。
- 为 WASM 构建您的项目。使用 cargo tree 使其正常工作。
- 创建 WASM 测试和一个 WASM 演示。
- [可选] 为嵌入式构建您的项目。
- [可选] 创建一个单独的嵌入式测试和一个嵌入式演示。
- 完成 CI 测试,Cargo.toml 元数据和更新的 README.md。
遵循这些规则将帮助您创建非常快速和内存安全的代码,它可以运行在从个人电脑到智能手机网页(演示),再到机器人的任何地方。这段代码可以非常精简,并且可以利用庞大的 Rust crate 库。
为了说明这些规则,我们将把 range-set-blaze
crate 移植到网页(WASM)和微控制器(嵌入式)上运行。(这个 crate 操纵“杂乱”整数的集合。该 crate 的用户要求移植。)
将代码移植到 WASM 和嵌入式要求避免使用 Rust 的标准库“std”。转换为“no std”比我预期的更容易和更困难。更容易是因为您仍然可以使用 Vec
和 String
。更困难的是测试。根据我在 range-set-blaze
上的经验,这里是我推荐的逐个描述的决策。为了避免含糊不清,我将这些推荐表述为规则。
规则 1:将 lib.rs 或 main.rs 标记为 no_std。
先使用 Git 为您的项目创建一个新的分支。这样,如果事情不顺利,您可以轻松撤消所有更改。
在 lib.rs
的开头添加:
#![cfg_attr(not(test), no_std)]
这告诉 Rust 编译器在除了测试时不要包含标准库。
附注 1:我的项目是一个带有
lib.rs
的库项目。我认为带有main.rs
的二进制项目的步骤大致相同,但我没有测试过。附注 2:我们将在后面的规则中更详细地讨论代码测试。
将 “no_std” 行添加到 range-set-blaze
的 lib.rs
中会导致 40 个编译器问题,其中大部分是这种形式的问题:
通过在主要代码中更改“std::”为“core::”,可以修复其中一些问题(不包括测试代码)。对于range-set-blaze
,这将问题的数量从40减少到12。这种修复对于许多项目很有帮助,因为诸如std::cmp::max
之类的项也可以作为core::cmp::max
使用。
不幸的是,像Vec
和Box
这样的项不能在core
中,因为它们需要分配内存。然而,如果你愿意支持内存分配,仍然可以使用它们。
规则2:如果可以的话,请使用内置的“crate alloc”。
你是否应该允许你的crate分配内存?对于WASM,你应该。对于许多嵌入式应用程序,你也应该。然而,对于一些嵌入式应用程序,你不应该。如果你决定允许内存分配,那么在lib.rs
的顶部添加:
extern crate alloc;
现在,你可以添加以下行来访问许多分配内存的项:
extern crate alloc;use alloc::boxed::Box;use alloc::collections::btree_map;use alloc::collections::BTreeMap;use alloc::vec::Vec;use alloc::{format, string::String};use alloc::vec;
对于range-set-blaze
,这将问题的数量从12减少到2。我们将在规则3中修复这些问题。
注:如果你正在为一个无法使用内存分配的嵌入式环境编写代码,并且在例如
Vec
方面遇到问题,你可能能够进行重写。例如,你可以使用数组来代替向量。如果这样做不起作用,请查看其他规则。如果没有任何方法可行,则可能无法将你的crate移植到no_std
。
规则3:切换到“no std”依赖。
Rust编译器会抱怨你的项目使用了将“std”函数放入你的代码的crate。有时,你可以在crates.io上搜索并找到替代的“no_std” crate。例如,流行的thiserror
crate会将“std”注入你的代码。然而,社区已经创建了不需要注入“std”的替代品。
在range-set-blaze
的情况下,剩下的两个问题与crate gen_ops
有关——这是一个非常好用的crate,用于方便地定义诸如“+”和“&”等操作符。版本0.3.0的gen_ops
不完全支持“no std”。不过,版本0.4.0支持。我在Cargo.toml
中更新了我的依赖关系,并改进了“no std”的兼容性。
现在,我可以运行以下命令:
cargo check # 检查是否以no_std编译成功cargo test # 使用std运行测试,确保测试通过
命令cargo check
确认我的crate没有直接使用标准库。命令cargo test
确认我的测试(仍然使用标准库)仍然通过。如果你的crate仍然无法编译,请参考下一个规则。
规则4:创建std和alloc功能,并使std-only函数可选。
嵌入式处理器通常不支持读写文件。同样,WASM还没有完全支持文件。虽然你可以找到一些与文件相关的“no std” crates,但似乎没有一个全面的。因此,如果文件IO对于你的crate是核心功能,将其移植到WASM和嵌入式环境可能不太实际。
然而,如果文件IO或任何其他仅限于std的函数只是你的crate的附属功能,你可以通过“std”功能将该函数设为可选。以下是方法:
将以下内容添加到您的 Cargo.toml
:
[package]#...resolver = "2" # Rust 2021+ 的默认设置[features]default = ["std"]std = []alloc = []
这表示您的 crate 现在具有两个特性,“std” 和 “alloc”。默认情况下,编译器应使用 “std”。
在您的 lib.rs
文件的顶部,将以下内容替换为:
#![cfg_attr(not(test), no_std)]
使用以下内容替换:
#![cfg_attr(not(feature = "std"), no_std)]
这表示如果您没有应用 “std” 特性,编译器应该在没有标准库的情况下进行编译。
在任何仅限于 std 的代码之前的行上,加上 #[cfg(feature = "std")]
。例如,这里我们定义了一个基于文件内容创建 RangeSetBlaze
结构的函数:
#[cfg(feature = "std")]use std::fs::File;#[cfg(feature = "std")]use std::io::{self, BufRead};#[cfg(feature = "std")]use std::path::Path;#[cfg(feature = "std")]#[allow(missing_docs)]pub fn demo_read_ranges_from_file<P, T>(path: P) -> io::Result<RangeSetBlaze<T>>where P: AsRef<Path>, T: FromStr + Integer,{ //...未显示的代码}
要检查 “std” 和 “alloc” 特性,可以执行以下操作:
cargo check # stdcargo check --features alloc --no-default-features
我们可以使用以下命令来测试 “std” 特性:
cargo test
附注:令人惊讶的是,
cargo test --features alloc --no-default-features
不会测试 “alloc” 特性。这是因为测试需要线程、分配和其他在no_std
中可能不可用的东西,所以 cargo 总是将常规测试作为 “std” 运行。
到此为止,我们正在检查 “std” 和 “alloc” 两者,所以我们可以假设我们的库适用于 WASM 和嵌入式。不过,一般来说,没有经过测试的东西是不可靠的。具体来说,我们可能依赖于使用 “std” 代码的 crate。要找出这些问题,我们必须在 WASM 和嵌入式环境中进行测试。
规则 5:为 WASM 构建项目。使用 cargo tree 使其正常工作。
安装 WASM 交叉编译器,并使用以下命令检查您的项目:
rustup target add wasm32-unknown-unknown # 只需要执行一次# 可能会发现问题cargo check --target wasm32-unknown-unknown --features alloc --no-default-features
当我在 range-set-blaze
上执行此操作时,它报告 getrandom
crate 与 WASM 不兼容。一方面,我对 WASM 不完全支持随机数并不感到惊讶。另一方面,我感到惊讶是因为我的项目没有直接依赖于 getrandom
。为了找到间接依赖关系,我使用 cargo tree
。我发现我的项目依赖于 crate rand
,而 crate rand
又依赖于 getrandom
。以下是使用 cargo tree
命令的示例:
cargo tree --edges no-dev --format "{p} {f}" --features alloc --no-default-features
该命令输出了 crate 及其使用的特性:
range-set-blaze v0.1.6 (O:\Projects\Science\wasmetc\wasm3) alloc├── gen_ops v0.4.0├── itertools v0.10.5 default,use_alloc,use_std│ └── either v1.8.1 use_std├── num-integer v0.1.45 default,std│ └── num-traits v0.2.15 default,std│ [build-dependencies]│ └── autocfg v1.1.0│ [build-dependencies]│ └── autocfg v1.1.0├── num-traits v0.2.15 default,std (*)├── rand v0.8.5 alloc,default,getrandom,libc,rand_chacha,std,std_rng│ ├── rand_chacha v0.3.1 std│ │ ├── ppv-lite86 v0.2.17 simd,std│ │ └── rand_core v0.6.4 alloc,getrandom,std│ │ └── getrandom v0.2.9 std│ │ └── cfg-if v1.0.0...
输出显示range-set-blaze
依赖于rand
。同时,它显示rand
依赖于getrandom
及其“std”特性。
我阅读了getrandom
的文档并了解到它的“js”特性支持WASM。那么,我们如何告诉rand
使用getrandom/js
,但仅在使用“alloc”特性进行编译时呢?我们按照以下方式更新我们的Cargo.toml
:
[features]default = ["std"]std = ["getrandom/std"]alloc = ["getrandom/js"][dependencies]# ...getrandom = "0.2.10"
这表示我们的“std”特性依赖于getrandom
的“std”特性。然而,我们的“alloc”特性应该使用getrandom
的“js”特性。
现在这个工作正常了:
cargo check --target wasm32-unknown-unknown --features alloc --no-default-features
所以,我们已经编译了WASM,但是关于测试WASM呢?
规则6:创建WASM测试和WASM演示。
让我们先用测试,然后再用演示网页来使用WASM版本。
在tests/wasm.rs
中创建WASM测试
你可以与本地测试几乎一样轻松地进行WASM测试。我们通过让原始测试仅在本地运行,同时在WASM上运行几乎相同的测试来实现这一点。下面是基于wasm-bindgen
指南的步骤:
- 执行
cargo install wasm-bindgen-cli
- 将当前的集成测试从
tests/integration_tests.rs
(例如)复制到tests/wasm.rs
。(回忆一下,在Rust中,集成测试是位于src
目录之外的测试,只能访问项目的公共方法。) - 在
tests/wasm.rs
的顶部,移除#![cfg(test)]
,并添加#![cfg(target_arch = “wasm32”)]
和use wasm_bindgen_test::*; wasm_bindgen_test_configure!(run_in_browser);
- 在
wasm.rs
中,将所有的#[test]
替换为#[wasm_bindgen_test]
- 在所有的
#![cfg(test)]
处(通常在tests/integration_tests.rs
和src/tests.rs
),添加额外的一行:#![cfg(not(target_arch = "wasm32"))]
- 在
Cargo.toml
中,将[dev-dependencies]
(如果有)更改为[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
- 在
Cargo.toml
中,添加一个部分:
[target.'cfg(target_arch = "wasm32")'.dev-dependencies]wasm-bindgen-test = "0.3.37"
设置好所有这些后,本地测试cargo test
应该仍然有效。如果你没有安装Chrome浏览器,请安装它。现在尝试使用以下命令来运行WASM测试:
wasm-pack test --chrome --headless --features alloc --no-default-features
这可能会失败,因为你的WASM测试使用了尚未或无法放入Cargo.toml
的依赖项。解决每个问题,可以选择:
- 将所需的依赖项添加到
Cargo.toml
的[target.'cfg(target_arch = "wasm32")'.dev-dependencies]
部分,或 - 从
tests/wasm.rs
中移除这些测试。
</
对于 range-set-blaze
,我移除了所有与测试该包的基准测试框架相关的WASM测试。这些测试仍然会在本地运行。在 tests\wasm.rs
中有一些有用的测试需要 crate syntactic-for
,所以我将其添加到 Cargo.toml
的 [target.'cfg(target_arch = "wasm32")'.dev-dependencies]
下面。修复后,所有的59个WASM测试都能够运行并通过。
附注:如果你的项目包括一个 examples 文件夹,你可能需要在例子中创建一个
native
模块和一个wasm
模块。参考这个range-set-blaze
文件,了解如何操作。
在 tests/wasm-demo
中创建一个WASM演示
支持WASM的有趣之处之一是可以在网页中演示你的Rust代码。这里有一个 range-set-blaze
的网页演示。
按照以下步骤创建你自己的网页演示:
在你项目的主 Cargo.toml
文件中,定义一个工作空间,并将 tests/wasm-demo
添加到其中:
[workspace]members = [".", "tests/wasm-demo"]
在你的 tests 文件夹中,创建一个 test/wasm-demo
子文件夹。
它应该包含一个新的 Cargo.toml
文件,内容如下(将 range-set-blaze
改为你项目的名称):
[package]name = "wasm-demo"version = "0.1.0"edition = "2021"[lib]crate-type = ["cdylib"][dependencies]wasm-bindgen = "0.2"range-set-blaze = { path = "../..", features = ["alloc"], default-features = false}
此外,创建一个文件 tests/wasm-demo/src/lib.rs
。这是我的文件内容:
#![no_std]extern crate alloc;use alloc::{string::ToString, vec::Vec};use range_set_blaze::RangeSetBlaze;use wasm_bindgen::prelude::*;#[wasm_bindgen]pub fn disjoint_intervals(input: Vec<i32>) -> JsValue { let set: RangeSetBlaze<_> = input.into_iter().collect(); let s = set.to_string(); JsValue::from_str(&s)}
这个文件定义了一个名为 disjoint_intervals
的函数,它接受一个整数向量作为输入,例如 100,103,101,102,-3,-4
。使用 range-set-blaze
包,该函数返回一串排序后的不相交区间的整数,例如 -4..=-3, 100..=103
。
作为最后一步,创建文件 tests/wasm-demo/index.html.
我的文件使用一小段JavaScript来接受一个整数列表,然后调用Rust的WASM函数 disjoint_intervals
。
<!DOCTYPE html><html><body> <h2>Rust WASM RangeSetBlaze Demo</h2> <p>输入一个以逗号分隔的整数列表:</p> <input id="inputData" type="text" value="100,103,101,102,-3,-4" oninput="callWasmFunction()"> <br><br> <p id="output"></p> <script type="module"> import init, { disjoint_intervals } from './pkg/wasm_demo.js'; function callWasmFunction() { let inputData = document.getElementById("inputData").value; let data = inputData.split(',').map(x => x.trim() === "" ? NaN : Number(x)).filter(n => !isNaN(n)); const typedArray = Int32Array.from(data); let result = disjoint_intervals(typedArray); document.getElementById("output").innerHTML = result; } window.callWasmFunction = callWasmFunction; init().then(callWasmFunction); </script></body></html>
要在本地运行演示,请首先将终端移动到tests/wasm-demo
。然后执行以下操作:
# 从 tests/wasm-demowasm-pack build --target web
接下来,启动本地的 Web 服务器并查看页面。我使用 Live Preview 扩展程序来预览 VS Code。许多人使用python -m http.server
。这是 range-set-blaze
演示的样子(也可以在 GitHub 上实时查看):
我发现在网页中运行我的 Rust 项目非常令人满意。如果只需要 WASM 兼容性,您可以跳到第 9 条规则。
规则 7:为嵌入式构建项目。
如果您想让项目更进一步,超越 WASM,请遵循这条规则和下一条规则。
确保将终端移回项目的主目录。然后,使用以下命令安装流行的嵌入式处理器thumbv7m-none-eabi
并检查项目:
# 从项目的主目录rustup target add thumbv7m-none-eabi # 只需要执行一次# 可能会发现问题cargo check --target thumbv7m-none-eabi --features alloc --no-default-features
当我在 range-set-blaze
上执行此操作时,会出现与四个依赖项相关的错误:
thiserror
— 我的项目依赖于这个 crate,但实际上并没有使用它。我移除了该依赖项。rand
和getrandom
— 我的项目只需要用于本地测试的随机数,所以我将依赖项移动到[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]
。我还更新了主要代码和测试代码。itertools
、num-traits
和num-integer
— 这些 crate 提供了“std”和“alloc”的功能。我更新了Cargo.toml
,如下所示:
...[features]default = ["std"]std = ["itertools/use_std", "num-traits/std", "num-integer/std"]alloc = ["itertools/use_alloc", "num-traits", "num-integer"][dependencies]itertools = { version = "0.10.1", optional = true, default-features = false }num-integer = { version = "0.1.44", optional = true, default-features = false }num-traits = { version = "0.2.15", optional = true, default-features = false }gen_ops = "0.4.0"[target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies]#...rand = "0.8.4"#...
我如何知道使用哪个依赖项的哪个功能?了解 crate 的功能(如 itertools
)需要阅读其文档,并且通常需要前往其 GitHub 存储库并阅读其Cargo.toml
。您还应该使用cargo tree
检查每个依赖项是否提供了所需的功能。例如,这是使用cargo tree
显示默认编译时的range-set-blaze
、num-integer
和 num-traits
的“std”功能以及itertools
和 either
的“use-std”功能的示例:
cargo tree --edges no-dev --format "{p} {f}"
range-set-blaze v0.1.6 (O:\Projects\Science\wasmetc\wasm4) default,itertools,num-integer,num-traits,std├── gen_ops v0.4.0├── itertools v0.10.5 use_alloc,use_std│ └── either v1.8.1 use_std├── num-integer v0.1.45 std│ └── num-traits v0.2.15 std│ [build-dependencies]│ └── autocfg v1.1.0│ [build-dependencies]│ └── autocfg v1.1.0└── num-traits v0.2.15 std (*)
这表明,在使用 --features alloc --no-default-feature
进行编译时,我得到了所需的 itertools
的“use_alloc”特性和其他依赖项的“no default”版本:
cargo tree --edges no-dev --format "{p} {f}" --features alloc --no-default-features
range-set-blaze v0.1.6 (O:\Projects\Science\wasmetc\wasm4) alloc,itertools,num-integer,num-traits├── gen_ops v0.4.0├── itertools v0.10.5 use_alloc│ └── either v1.8.1├── num-integer v0.1.45│ └── num-traits v0.2.15│ [build-dependencies]│ └── autocfg v1.1.0│ [build-dependencies]│ └── autocfg v1.1.0└── num-traits v0.2.15 (*)
当您认为一切都运行正常时,使用以下命令检查/测试本地、WASM 和嵌入式:
# 测试本地cargo testcargo test --features alloc --no-default-features# 检查和测试WASMcargo check --target wasm32-unknown-unknown --features alloc --no-default-featureswasm-pack test --chrome --headless --features alloc --no-default-features# 检查嵌入式cargo check --target thumbv7m-none-eabi --features alloc --no-default-features
这些命令检查嵌入式,但如何测试嵌入式?
规则8:创建一个嵌入式测试和一个嵌入式演示。
让我们利用我们的嵌入式功能创建一个组合的测试和演示。我们将在一个名为QEMU的模拟器上运行它。
测试本地的Rust很容易。测试WASM的Rust还可以。测试嵌入式的Rust很难。我们只做一个简单的测试。
注1:有关运行和仿真嵌入式Rust的更多信息,请参阅《嵌入式Rust Book》。
注2:有关嵌入式Rust更完整的测试框架的想法,请参阅defmt-test。不幸的是,我无法弄清楚如何在仿真下运行它。cortex-m/testsuite项目使用了defmt-test的fork,并且可以在仿真下运行,但不提供独立的测试crate,并且需要三个额外的(子)项目。
注3:一个嵌入式测试总比没有测试好。我们将在本地和WASM级别进行其余的测试。
我们将在当前的tests
文件夹中创建嵌入式测试和演示。文件如下:
tests/embedded├── .cargo│ └── config.toml├── Cargo.toml├── build.rs├── memory.x└── src └── main.rs
以下是创建文件和设置的步骤。
- 安装QEMU模拟器。在Windows上,这涉及运行安装程序,然后手动将
"C:\Program Files\qemu\"
添加到您的路径。
2. 创建一个依赖于您的本地项目的tests/embedded/Cargo.toml
文件,其中不带默认功能且带有“alloc”。这是我的文件:
[package]edition = "2021"name = "embedded"version = "0.1.0"[dependencies]alloc-cortex-m = "0.4.4"cortex-m = "0.6.0"cortex-m-rt = "0.6.10"cortex-m-semihosting = "0.3.3"panic-halt = "0.2.0"# 引用您的本地项目range-set-blaze = { path = "../..", features = ["alloc"], default-features = false }[[bin]]name = "embedded"test = falsebench = false
3. 创建一个tests/embedded/src/main.rs
文件。在“test goes here”注释后放置您的测试代码。这是我的文件:
// 基于https://github.com/rust-embedded/cortex-m-quickstart/blob/master/examples/allocator.rs// 和https://github.com/rust-lang/rust/issues/51540#![feature(alloc_error_handler)]#![no_main]#![no_std]extern crate alloc;use alloc::string::ToString;use alloc_cortex_m::CortexMHeap;use core::{alloc::Layout, iter::FromIterator};use cortex_m::asm;use cortex_m_rt::entry;use cortex_m_semihosting::{debug, hprintln};use panic_halt as _;use range_set_blaze::RangeSetBlaze;#[global_allocator]static ALLOCATOR: CortexMHeap = CortexMHeap::empty();const HEAP_SIZE: usize = 1024; // in bytes#[entry]fn main() -> ! { unsafe { ALLOCATOR.init(cortex_m_rt::heap_start() as usize, HEAP_SIZE) } // test goes here let range_set_blaze = RangeSetBlaze::from_iter([100, 103, 101, 102, -3, -4]); assert!(range_set_blaze.to_string() == "-4..=-3, 100..=103"); hprintln!("{:?}", range_set_blaze.to_string()).unwrap(); // exit QEMU/ NOTE do not run this on hardware; it can corrupt OpenOCD state debug::exit(debug::EXIT_SUCCESS); loop {}}#[alloc_error_handler]fn alloc_error(_layout: Layout) -> ! { asm::bkpt(); loop {}}
4. 从cortex-m-quickstart的GitHub上复制build.rs
和memory.x
到tests/embedded/
。
5. 创建一个tests/embedded/.cargo/config.toml
文件,其中包含:
[target.thumbv7m-none-eabi]
runner = "qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel"
[build]
target = "thumbv7m-none-eabi"
6. 通过将tests/embedded
添加到工作空间中,更新项目的主要Cargo.toml
:
[workspace]
members = [".", "tests/wasm-demo", "tests/embedded"]
通过这样的设置,您几乎可以准备好运行仿真嵌入式系统了。接下来,将终端放在适当位置,并将编译器设置为夜间版本:
# 确保qemu在路径中,例如,设置路径为“C:\Program Files\qemu\”;%PATH%
cd tests/embedded
rustup override set nightly # 以支持#![feature(alloc_error_handler)]
现在,您可以在演示应用程序上使用cargo check
、cargo build
和cargo run
。例如:
cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.03s Running `qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic -semihosting-config enable=on,target=native -kernel O:\Projects\Science\wasmetc\wasm4\target\thumbv7m-none-eabi\debug\embedded`Timer with period zero, disabling"-4..=-3, 100..=103"
当您成功运行项目时,您将在(模拟的)微控制器上成功运行项目!如果在设置过程中遇到问题,请再次检查这些说明。如果还是不行,请参阅《嵌入式Rust书籍》。
完成后,请确保将编译器重新设置为稳定版本:
rustup override set stable
规则9:完成CI测试、Cargo.toml元数据和更新的README.md文件。
CI测试
我们快要完成了,但我们必须确保今天的所有工作明天仍然有效。这就是CI(持续集成)测试的工作。
我将CI测试设置为每次检查和每月运行一次。如果在GitHub上,请创建文件.github/workflows/tests.yml
:
name: test
on:
push:
schedule: # 每月运行一次
- cron: '0 0 1 * *'
pull_request:
workflow_dispatch:
jobs:
test_rust:
name: 测试Rust
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Setup Rust
uses: dtolnay/rust-toolchain@master
with:
toolchain: stable
- name: Setup WASM
uses: jetli/[email protected]
- name: Test Native & WASM
run: |
cargo clippy --verbose --all-targets --all-features -- -D warnings
cargo test --verbose
cargo test --features alloc --no-default-features --verbose
wasm-pack test --chrome --headless --features alloc --no-default-features --verbose
- name: Setup and check Embedded
run: |
rustup target add thumbv7m-none-eabi
cargo check --target thumbv7m-none-eabi --features alloc --no-default-features
rustup override set nightly
rustup target add thumbv7m-none-eabi
cargo check --target thumbv7m-none-eabi --features alloc --no-default-features
sudo apt-get update && sudo apt-get install qemu qemu-system-arm
- name: Test Embedded (in nightly)
timeout-minutes: 3
run: |
cd tests/embedded
cargo run
如果您只是在使用WASM,可以省略与嵌入式相关的最后两个步骤。
附言:为什么最后一个测试的
timeout-minutes: 3
呢?因为一个失败的嵌入式测试不会以失败结束,而是进入无限循环。我们通过超时来捕捉这个问题。
元数据
Rust 允许您将代码标记为适用于特定的架构和环境。约定是使用关键字和类别元数据。具体地,在适当的位置添加这些关键字和类别到您的 Cargo.toml
中:
[package]#...keywords = [#... "wasm", "no_std",]categories = [#... "wasm", "no-std",]
README.md
您还应该更新您的 README.md
来告诉人们您支持 WASM 和嵌入式开发。这是我添加的内容:
这个 crate 支持 no_std、WASM 和嵌入式项目:```toml[dependencies]range-set-blaze = { features = ["alloc"], default-features = false, version=VERSION }``` *将 VERSION 替换为当前版本。
所以,这就是 Rust 中用于 WASM 和 no_std 端口的九个规则。Rust 是一种非常适合原生、WASM 和嵌入式编程的语言。它提供了速度、安全性和对数以千计的有用 crate 的访问。按照这九个规则在几乎所有地方运行您自己的 Rust 代码。
附言:如果您对未来的文章感兴趣,请在 VoAGI 上关注我。我会写关于 Rust 和 Python 的科学编程、机器学习和统计的文章。我通常每个月写一篇文章。