├── .gitignore ├── Cargo.toml ├── README.md ├── a-no-std-rust-environment.md ├── create-no-std-lib-1 ├── Cargo.toml └── src │ └── lib.rs ├── create-no-std-lib-2 ├── Cargo.toml └── src │ └── lib.rs ├── rust-no-std-article.md ├── rust-no-std.md └── use-no-std ├── Cargo.toml └── src └── main.rs /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 6 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 7 | Cargo.lock 8 | 9 | # These are backup files generated by rustfmt 10 | **/*.rs.bk 11 | 12 | /example-no-std/target/* 13 | 14 | .idea -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "create-no-std-lib-1", 4 | "create-no-std-lib-2", 5 | "use-no-std", 6 | ] -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # rust-no-std-source 2 | > About Rust no std sources 3 | 4 | 5 | # 题目: 改写std的库为支持no_std的库及写出一个支持std和no_std库的经验谈 6 | 7 | >首先简单介绍std和no_std的区别, 8 | 然后介绍使用no_std库方式, 由于支持no_std的特性有两种不同的方式, 9 | 因此使用no_std库也有两种方式。 10 | 如何验证一个库是否支持no_std特性的验证方式, 11 | 如何改写一个std库为支持std和no_std的特性的方法。 12 | 具体的如何写一个支持std和no_std的库。 13 | 一些在std和no_std下都可以使用的primitive的仓库和相关的资源和文章。 14 | 15 | 16 | 17 | # 目录 18 | 19 | - std和no_std的区别 20 | - Rust中使用no_std库的两种方式 21 | - 验证一个库是否支持no_std特性的验证方式 22 | - 具体的写一个支持std和no_std的库 23 | - 一些no_std和std可以使用primitive类型的仓库和相关资源的文章 24 | 25 | 26 | 27 | 28 | 29 | ## 1. std 和 no_std 的区别 30 | 31 | > ### 核心库 32 | > 33 | > Rust语言的语法由核心库和标准库共同提供。 34 | > 其中Rust核心库是标准库的基础。核心库中定义的是Rust语言的核心,不依赖于操作系统和网络等相关的库,甚至不知道堆分配,也不提供并发和I/O 35 | > 36 | > 可以通过在模块顶部引入#![no_std]来使用核心库。核心库和标准库的功能有一些重复,包括如下部分: 37 | > - 基础的trait,如Copy、Debug、Display、Option等。 38 | > - 基本原始类型,如bool、char、i8/u8、i16/u16、i32/u32、i64/u64、isize/usize、f32/f64、str、array、slice、tuple、pointer等。 39 | > - 常用功能型数据类型,满足常见的功能性需求,如String、Vec、HashMap、Rc、Arc、Box等。 40 | > - 常用的宏定义,如println!、assert!、panic!、vec!等。 41 | > 做嵌入式应用开发的时候,核心库是必需的。 42 | > 43 | > ### 标准库 44 | > 45 | > Rust标准库提供应用程序开发所需要的基础和跨平台支持。标准库包含的内容大概如下: 46 | > - 与核心库一样的基本trait、原始数据类型、功能型数据类型和常用宏等,以及与核心库几乎完全一致的API。 47 | > - 并发、I/O和运行时。例如线程模块、用于消息传递的通道类型、Sync trait等并发模块,文件、TCP、UDP、管道、套接字等常见I/O。 48 | > - 平台抽象。os模块提供了许多与操作环境交互的基本功能,包括程序参数、环境变量和目录导航;路径模块封装了处理文件路径的平台特定规则。 49 | > - 底层操作接口,比如 std::mem、std::ptr、std::intrinsics 等,操作内存、指针、调用编译器固有函数。 50 | > - 可选和错误处理类型Option和Result,以及各种迭代器等。 51 | 52 | 53 | [引用自The Embedonomicon](https://docs.rust-embedded.org/embedonomicon/smallest-no-std.html) 54 | `#![no_std]`是一个crate level 级别的属性, 55 | 表示该core crate将链接到core crate而不是std crate, 56 | 但这对应用程序来说意味着什么呢? 57 | 58 | std crate是Rust的标准库。 59 | 它包含的功能假定程序将在操作系统上运行, 60 | 而不是直接在裸系统上运行。 61 | std还假定操作系统是一个通用的操作系统, 62 | 就像人们在服务器和台式机上看到的那样。 63 | 出于这个原因,std为通常在这类操作系统中发现的功能提供了一个标准的API: 线程、文件、套接字、文件系统、进程等等。 64 | 65 | 另一方面,core crate是std crate 的一个子集, 66 | 对程序运行的系统不做任何假设。 67 | 因此,它提供了基本语言的API,如浮点、字符串和切片, 68 | 以及暴露处理器特性的API,如原子操作和SIMD指令。 69 | 然而,它缺乏涉及堆内存分配和I/O的任何API。 70 | 71 | 对于一个应用程序来说, 72 | std所做的不仅仅是提供一种访问操作系统抽象的方式, 73 | std还负责设置堆栈溢出保护,处理命令行参数, 74 | 以及在程序的主函数被调用之前生成主线程。 75 | 一个`#![no_std]`应用程序缺乏所有这些标准的运行时间, 76 | 所以它必须初始化自己的运行时间,如果需要的话。 77 | 78 | 由于这些特性,`#![no_std]`应用程序可以是第一个和/或唯一在系统上运行的代码。它可以成为许多标准的Rust应用程序所不可能成为的东西,例如。 79 | - 操作系统的内核。 80 | - 固件。 81 | - 引导器。 82 | 83 | 84 | ## 2. Rust中no_std的一些使用方法 85 | 86 | 参见4: 具体的写一个no_std的库 87 | 88 | 实例:[serde no-std的使用规范](https://serde.rs/no-std.html) 89 | 90 | ## 3. 验证一个库是否支持no_std的验证方式 91 | 92 | ` 93 | cargo check --target wasm32-unknown-unknown 94 | ` 95 | 96 | 但是wasm环境不一定就是no_std,或者别的编译目标也可以,也就是裸露的编译目标环境不带有任何系统的环境。 97 | 98 | 参考文档: [使用Rust编写操作系统(一):独立式可执行程序](https://zhuanlan.zhihu.com/p/53064186) 99 | 100 | 101 | ## 4. 具体的写一个no_std的库 102 | 103 | ### 创建一个no_std库的第一种方式 (使用#![no_std]) 104 | 105 | 使用#![no_std]的话,默认的就是这个库是在no_std环境下的,然而又因为no_std下的库 106 | 一般来说都是核心库,而核心库又是标准库的子集,所以声明#![no_std]写出来的库,也可以在 107 | std(标准库环境)下使用。 108 | 109 | 1. [创建一个仓库](https://github.com/DaviRain-Su/rust-no-std-source/commit/cd90f28855cfe794c235976bb58c1c5ecb8c7fa9) 110 | 111 | 2. [使用#![no_std]将这个仓库中的函数能支持在no_std和std下使用](https://github.com/DaviRain-Su/rust-no-std-source/commit/d3c05920865a44ab7cbaf82a72f21c7b6b8beeb0) 112 | 113 | 3. [开始添加一个函数编译报错commit 1](https://github.com/DaviRain-Su/rust-no-std-source/commit/8bcd0b909ee116d3dc9c6464c2548e1c008d672e) 114 | 115 | 4. [修复错误commit 2](https://github.com/DaviRain-Su/rust-no-std-source/commit/ae94f9cf147b7ce37632cb4e9c36e20c5135b3ad) 116 | 117 | 118 | ### 创建no_std库的第二种方式(使用#![cfg_attr(not(feature = "std"), no_std)) 119 | 120 | 1. [创建一个仓库](https://github.com/DaviRain-Su/rust-no-std-source/commit/8cfd063be536028d9f8cfe1c7c04f16765825f8c) 121 | 2. [使用#![cfg_attr(not(feature = "std"), no_std)]](https://github.com/DaviRain-Su/rust-no-std-source/commit/aa09b0d2e2807d788564aea5fa4fc8cbfc760043) 122 | 3. [添加的一些函数和测试](https://github.com/DaviRain-Su/rust-no-std-source/commit/aa09b0d2e2807d788564aea5fa4fc8cbfc760043) 123 | 124 | ## 5 使一些不能在no_std环境下运行的仓库也能在no_std下支持 125 | 126 | [相关的Pr,使ics23支持no_std](https://github.com/confio/ics23/pull/41) 127 | 128 | [有些代码也在no_std写测试很难。因为这里做了编译选择处理](https://github.com/confio/ics23/pull/41/commits/dac5c3d0fc368e0b92c4a4804b6787bd1c3fb168) 129 | 130 | 131 | ## 6. 一些no_std和std可以使用的primite类型仓库 132 | 133 | - [sp-std](https://github.com/paritytech/substrate/tree/master/primitives/std) 134 | - [rust Alloc crate](https://doc.rust-lang.org/alloc/index.html) 135 | - [rust Core crate](https://doc.rust-lang.org/core/index.html) 136 | 137 | ## 7, pallet-node-template 可以测试是否支持no_std特性 138 | - [pallet-node-template](https://github.com/DaviRain-Su/pallet-node-template) 139 | 140 | 141 | 142 | ## 引用 143 | 144 | [rust编程之道核心库和标准库的介绍](https://weread.qq.com/web/reader/0303203071848774030b9d6k9bf32f301f9bf31c7ff0a60) 145 | 146 | [rust embeded book](https://docs.rust-embedded.org/book/intro/no-std.html) 147 | 148 | [扩展no_std crate的最佳实践](https://users.rust-lang.org/t/best-practice-of-extending-a-no-std-crate/12281/5) 149 | 150 | [Rust API guidelines](https://github.com/rust-lang/api-guidelines) 151 | 152 | [Rust API Guidelines Naming](https://rust-lang.github.io/api-guidelines/naming.html#c-feature) 153 | 154 | [serde no-std的使用规范](https://serde.rs/no-std.html) 155 | 156 | [awesome-embedded-rust#no-std-crates](https://github.com/rust-embedded/awesome-embedded-rust#no-std-crates) 157 | 158 | [no standard library](https://crates.io/categories/no-std) 159 | 160 | [serde使用的第二种方式](https://github.com/serde-rs/serde/blob/master/serde/src/lib.rs#L113-L193) 161 | 162 | [Rust RFC Book no_std](https://rust-lang.github.io/rfcs/1184-stabilize-no_std.html) 163 | 164 | [Rust no-std DAQ](https://justjjy.com/Rust-no-std) 165 | 166 | [testing-for-no-std-compatibility](https://blog.dbrgn.ch/2019/12/24/testing-for-no-std-compatibility/) 167 | -------------------------------------------------------------------------------- /a-no-std-rust-environment.md: -------------------------------------------------------------------------------- 1 | # 一个no_std的Rust环境 2 | 3 | 嵌入式编程这一术语被用于广泛的不同类别的编程。 4 | 从只有几KB的RAM和ROM的8位MCU(如ST72325xx)的编程, 5 | 到像Raspberry Pi(Model B 3+)这样拥有32/64位4核Cortex-A53 @ 1.4 GHz和1GB RAM的系统。 6 | 根据你的目标和使用情况,在编写代码时将有不同的限制/约束。 7 | 8 | 有两种通用的嵌入式编程分类。 9 | 10 | ## 托管环境 11 | 12 | 这类环境接近于正常的PC环境。 13 | 这意味着你被提供了一个系统接口,如POSIX,它为你提供了与各种系统交互的基元, 14 | 如文件系统、网络、内存管理、线程等。而标准库通常依赖于这些基元来实现其功能。 15 | 你也可能有某种系统根和对RAM/ROM使用的限制, 16 | 也许还有一些特殊的HW或I/O。 17 | 总的来说,这感觉就像在一个特殊用途的PC环境中进行编码。 18 | 19 | ## 裸金属环境 20 | 21 | 在裸机环境中,在你的程序之前没有加载任何代码。 22 | 没有操作系统提供的软件,我们就不能加载标准库。 23 | 相反,程序以及它所使用的板条, 24 | 只能使用硬件(裸机)来运行。为了防止Rust加载标准库, 25 | 请使用no_std。标准库中与平台无关的部分可以通过libcore获得。 26 | libcore也排除了一些在嵌入式环境中并不理想的东西。 27 | 其中之一是一个用于动态内存分配的内存分配器。 28 | 如果你需要这个或其他功能,通常会有提供这些功能的crack。 29 | 30 | ## libstd运行 31 | 如前所述,使用libstd需要某种系统集成,但这不仅是因为libstd只是提供了一种访问操作系统抽象的通用方法, 32 | 它还提供了一个运行时。 33 | 这个运行时,除其他事项外,还负责设置堆栈溢出保护, 34 | 处理命令行参数,以及在程序的主函数被调用前催生主线程。 35 | 在no_std环境下,这个运行时也是不可用的。 36 | 37 | ## 摘要 38 | 39 | `#![no_std]`是一个板块级属性,表示该板块将链接到core-rate而不是std-rate。 40 | 而libcore板块是std板块的一个平台无关的子集, 41 | 它不对程序将运行的系统进行假设。 42 | 因此,它提供了语言基元的API,如浮点、字符串和切片, 43 | 以及暴露处理器特性的API,如原子操作和SIMD指令。 44 | 然而,它缺乏任何涉及平台整合的API。 45 | 由于这些特性,no_std和libcore代码可用于任何类型的引导(阶段0)代码, 46 | 如引导程序、固件或内核。 47 | -------------------------------------------------------------------------------- /create-no-std-lib-1/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "create-no-std-lib-1" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | -------------------------------------------------------------------------------- /create-no-std-lib-1/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | extern crate alloc; 3 | use alloc::string::String; 4 | use alloc::format; 5 | 6 | pub fn sum(a: i32, b: i32) -> i32 { 7 | a + b 8 | } 9 | 10 | 11 | pub fn get_hello_string(value: &str) -> String { 12 | String::from(value) 13 | } 14 | 15 | 16 | 17 | #[cfg(test)] 18 | mod tests { 19 | use super::*; 20 | use alloc::string::String; 21 | use alloc::string::ToString; 22 | 23 | #[test] 24 | fn it_works() { 25 | assert_eq!(2 + 2, 4); 26 | } 27 | 28 | #[test] 29 | fn test_sum() { 30 | assert_eq!(sum(2, 3), 5); 31 | } 32 | 33 | #[test] 34 | fn test_get_hello_string() { 35 | assert_eq!("hello".to_string(), get_hello_string("hello")); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /create-no-std-lib-2/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "create-no-std-lib-2" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | # 使用sp_std下的类型,可以在no_std和std下都能编译通过 10 | sp-std = { version = "3.0.0", default-featuers = false } 11 | 12 | # 这里说明的是,default设置默认的featuer这里默认设置的是std, 13 | # 这里的std = []表示的意思,这个库create-no-std-lib-2可能需要一些别的库, 14 | # 当使用别的库的时候也有std和no_std的区分,当设置为default的时候,这个库create-no-std-lib-2 15 | # 就是使用的std的环境,因为这个库依赖的一些子库也需要std。而这里的std = []就是设置的。 16 | [features] 17 | default = ["std"] 18 | std = [] 19 | -------------------------------------------------------------------------------- /create-no-std-lib-2/src/lib.rs: -------------------------------------------------------------------------------- 1 | // 添加cfg_attr宏可以使得这个库可以在std的环境和no_std的环境之间转换 2 | // 这句话表达的意思是当不是std的时候启用的是no_std 3 | // 在cargo.toml中的配置使用这个crate的时候,将default-features 设置为false,就是将默认的std环境关闭启动的是no_std 4 | #![cfg_attr(not(feature = "std"), no_std)] 5 | 6 | extern crate alloc; 7 | use alloc::string::String; 8 | use sp_std::vec::Vec; 9 | 10 | 11 | pub fn hello(value: &str) -> Vec { 12 | let tmp = String::from(value); 13 | tmp.into_bytes() 14 | } 15 | 16 | #[cfg(test)] 17 | mod tests { 18 | use crate::hello; 19 | use alloc::string::{ToString, String}; 20 | 21 | #[test] 22 | fn it_works() { 23 | assert_eq!(2 + 2, 4); 24 | } 25 | 26 | #[test] 27 | fn test_hello() { 28 | let left = "hello".to_string(); 29 | let right = String::from_utf8(hello(left.as_str())).unwrap(); 30 | assert_eq!(left, right); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /rust-no-std-article.md: -------------------------------------------------------------------------------- 1 | # Rust no-std 工程实践 2 | 3 | > 改写std的库为支持no_std的库及写出一个支持std和no_std库的经验谈 4 | > github repo: https://github.com/DaviRain-Su/rust-no-std-source 5 | 6 | ## 简介 7 | 8 | 首先介绍std和no_std的区别,然后介绍使用no_std库的方式,由于支持no_std的特性有两种不同的方式,因此使用no_std库也有两种方式。其次,验证一个库是否支持no_std特性的验证方式,如何改写一个std的库为同时支持std和no_std的特性方法。具体的如何写一个支持std和no_std的库。一些在std和no_std下都可以使用的primitive的仓库和相关的资源和文章。 9 | 10 | ## 目录 11 | 12 | - std和no_std的区别 13 | - Rust中使用no_std的两种方式 14 | - 验证一个库是否支持no_std特性的验证方式 15 | - 具体的写一个支持std和no_std的库 16 | - 一些no_std和std可以使用primitive类型的仓库和相关资源的文章 17 | 18 | ## std和no_std的区别 19 | 20 | > ## 核心库 21 | > Rust语言的语法由核心库和标准库共同提供。 22 | > 其中Rust核心库是标准库的基础。核心库中定义的是Rust语言的核心,不依赖于操作系统和网络等相关的库,甚至不知道堆分配,也不提供并发和I/O 23 | > 24 | > 可以通过在模块顶部引入#![no_std]来使用核心库。核心库和标准库的功能有一些重复,包括如下部分: 25 | > 26 | > - 基础的trait,如Copy、Debug、Display、Option等。 27 | > - 基本原始类型,如bool、char、i8/u8、i16/u16、i32/u32、i64/u64、isize/usize、f32/f64、str、array、slice、tuple、pointer等。 28 | > - 常用功能型数据类型,满足常见的功能性需求,如String、Vec、HashMap、Rc、Arc、Box等。 29 | > - 常用的宏定义,如println!、assert!、panic!、vec!等。 做嵌入式应用开发的时候,核心库是必需的。 30 | > ## 标准库 31 | > Rust标准库提供应用程序开发所需要的基础和跨平台支持。标准库包含的内容大概如下: 32 | > - 与核心库一样的基本trait、原始数据类型、功能型数据类型和常用宏等,以及与核心库几乎完全一致的API。 33 | > - 并发、I/O和运行时。例如线程模块、用于消息传递的通道类型、Sync trait等并发模块,文件、TCP、UDP、管道、套接字等常见I/O。 34 | >- 平台抽象。os模块提供了许多与操作环境交互的基本功能,包括程序参数、环境变量和目录导航;路径模块封装了处理文件路径的平台特定规则。 35 | >- 底层操作接口,比如 std::mem、std::ptr、std::intrinsics 等,操作内存、指针、调用编译器固有函数。 36 | >- 可选和错误处理类型Option和Result,以及各种迭代器等。 37 | 38 | 还有一些解释,#![no_std] 是一个crate level 级别的属性,表示core crate将链接到core crate而不是std crate。 39 | 40 | 下面是std crate和core crate的解释,其实这里也就解释了标准库与和核心库之间的区别。当让也内在的包括了std与no_std之间的区别。 41 | 42 | 首先是,std crate 是Rust 的标准库。它包含的功能假定程序将在操作系统上运行,而不是直接在裸系统上运行。std还假定操作系统是一个通用的操作系统,就像人们在服务器和台式机上看到的那样。出于这个原因,std为通常在这类操作系统中发现的功能提供了一个标准的API: 线程、文件、套接字、文件系统、进程等等。 43 | 44 | 然后是,core crate是std crate的一个子集,对程序运行的系统不做任何假设。因此它提供了基于语言的API,如浮点,字符串和切片,以及暴露处理器特性的API,如原子操作和SIMD指令。然而,它缺乏涉及堆内存分配和I/O的任何API。 45 | 46 | 对于一个应用程序来说,std所做的不仅仅是提供一种访问操作系统抽象的方式,std还负责涉及堆栈溢出保护,处理命令行参数,以及在程序的主函数被调用之前生成主线程。一个#![no_std]应用程序缺乏所有这些标准的运行时,所以它必须初始化自己的运行时,如果需要的话。 47 | 48 | 由于这些特性,#![no_std]应用程序可以是第一个或者唯一在系统上运行的代码。 49 | 50 | ## Rust中no_std的一些使用方法 51 | 52 | 主要具体介绍第二种方式的使用no_std 53 | 54 | 具体如何使用,参见写一个no_std的库的第二种使用方式。 55 | 56 | 也可参考,实例:[serde no-std的使用规范](https://serde.rs/no-std.html) 57 | 58 | ## 验证一个库是否支持no_std的验证方式 59 | 60 | `cargo check --target wasm32-unknown-unknown` 61 | 62 | 但是wasm环境不一定就是no_std,或者别的编译目标也可以,也就是裸露的编译目标环境不带有任何系统的环境。 63 | 64 | 参考文档: [使用Rust编写操作系统(一):独立式可执行程序](https://zhuanlan.zhihu.com/p/53064186) 65 | 66 | ## 具体的写一个no_std的库 67 | 68 | ### 创建一个no_std库的第一种方式(使用#![no_std]) 69 | 70 | 使用#![no_std]的话,默认的就是这个库是在no_std环境下的,然而又因为no_std下的库 一般来说都是核心库,而核心库又是标准库的子集,所以声明#![no_std]写出来的库,也可以在 std(标准库环境)下使用。 71 | 72 | 1. [创建一个仓库](https://github.com/DaviRain-Su/rust-no-std-source/commit/cd90f28855cfe794c235976bb58c1c5ecb8c7fa9) 73 | 2. [使用#![no_std]将这个仓库中的函数能支持在no_std和std下使用](https://github.com/DaviRain-Su/rust-no-std-source/commit/d3c05920865a44ab7cbaf82a72f21c7b6b8beeb0) 74 | 3. [开始添加一个函数编译报错commit 1](https://github.com/DaviRain-Su/rust-no-std-source/commit/8bcd0b909ee116d3dc9c6464c2548e1c008d672e) 75 | 4. [修复错误commit 2](https://github.com/DaviRain-Su/rust-no-std-source/commit/ae94f9cf147b7ce37632cb4e9c36e20c5135b3ad) 76 | 77 | ### 创建no_std库的第二种方式(使用#![cfg_attr(not(features = "std"), no_std)] ) 78 | 79 | 1. [创建一个仓库](https://github.com/DaviRain-Su/rust-no-std-source/commit/8cfd063be536028d9f8cfe1c7c04f16765825f8c) 80 | 2. [使用#![cfg_attr(not(feature = "std"), no_std)]](https://github.com/DaviRain-Su/rust-no-std-source/commit/aa09b0d2e2807d788564aea5fa4fc8cbfc760043) 81 | 3. [添加的一些函数和测试](https://github.com/DaviRain-Su/rust-no-std-source/commit/aa09b0d2e2807d788564aea5fa4fc8cbfc760043) 82 | 83 | ## 使一些不能在no_std环境下运行的仓库也能在no_std下支持 84 | 85 | 首先,要验证这个库能不能支持no_std的环境(见,验证一个库是否支持no_std的验证方式)。 86 | 87 | 找出这个库依赖的库支持no_std的方式,如果使用的是#![no_std] 那么这个库本身就是可以在std和no_std下同时的运行。 88 | 89 | 如果使用的是#![cfg_attr(not(features = "std"), no_std)],就需要打开default-features = false,进行配置。 90 | 91 | 最后可能需要做一些标准库的替换,使其能在no_std和std同时编译成功,一些可以使用的类型库有sp-std(这个库仅仅封装了一部分的类型,例如有些类型是没有的,string,File, IO)当然,IO,File,这些标准库在核心库当中是没有的。还有rust本身的alloc, core这些都是属于核心库的。也是在no_std环境下支持的。 92 | 93 | 具体的使用案例: 94 | 95 | [相关的Pr,使ics23支持no_std](https://github.com/confio/ics23/pull/41) 96 | 97 | [有些代码也在no_std写测试很难。因为这里做了编译选择处理](https://github.com/confio/ics23/pull/41/commits/dac5c3d0fc368e0b92c4a4804b6787bd1c3fb168) 98 | 99 | ## 一些no_std和std可以使用的primitive 类型的仓库 100 | 101 | - [sp-std](https://github.com/paritytech/substrate/tree/master/primitives/std) 102 | - [rust Alloc crate](https://doc.rust-lang.org/alloc/index.html) 103 | - [rust Core crate](https://doc.rust-lang.org/core/index.html) 104 | 105 | ## 引用及资源 106 | 107 | - [Rust 编程之道核心库和标准库的介绍](https://weread.qq.com/web/reader/0303203071848774030b9d6k9bf32f301f9bf31c7ff0a60) 108 | - [Rust embeded book](https://docs.rust-embedded.org/book/intro/no-std.html) 109 | - [扩展no_std crate 的最佳实践](https://users.rust-lang.org/t/best-practice-of-extending-a-no-std-crate/12281/5) 110 | - [Rust API guidelines](https://github.com/rust-lang/api-guidelines) 111 | - [Rust API guidelines Nameing](https://rust-lang.github.io/api-guidelines/naming.html#c-feature) 112 | - [serde no_std的使用规范](https://serde.rs/no-std.html) 113 | - [awesome-embedded-rust#no-std-crates](https://github.com/rust-embedded/awesome-embedded-rust#no-std-crates) 114 | - [no standard library](https://crates.io/categories/no-std) 115 | - [serde使用的第二种方式](https://github.com/serde-rs/serde/blob/master/serde/src/lib.rs#L113-L193) 116 | - [Rust RFC Book no_std](https://rust-lang.github.io/rfcs/1184-stabilize-no_std.html) 117 | - [Rust no_std DAQ](https://justjjy.com/Rust-no-std) 118 | - [testing-for-no-std-compatibility](https://blog.dbrgn.ch/2019/12/24/testing-for-no-std-compatibility/) 119 | - [substrate中关于cfg_attr的介绍](https://substrate.dev/docs/zh-CN/tutorials/add-a-pallet/import-a-pallet) 120 | 121 | ## 结论 122 | 123 | 参照serder的使用以及一些论坛的讨论,推荐使用#![cfg_attr(not(feature = "std"), no_std ))]来同时支持std和no_std. -------------------------------------------------------------------------------- /rust-no-std.md: -------------------------------------------------------------------------------- 1 | # Rust no-std 的常见问题 2 | 3 | 4 | ## 什么要写这个? 5 | 6 | 大多数Rustaceans(包括我)自从学会了如何写`println!("hello world")`之后, 7 | 每天都在使用`std crate`。 8 | 但它仍然是Rust的一个非常重要的功能: 9 | 将我们的代码部署到裸机环境。 10 | 没有操作系统的支持,我们就不能使用`std crate`, 11 | 这通常会让人感到害怕,所以我写这篇文章来澄清对`Rust no-std`的大部分误解。 12 | 13 | ## 什么是Rust no-std? 14 | 15 | 在`std Rust`中,它是默认的学习版本。 16 | 我们可以做很多操作来与机器和互联网进行交互,比如向控制台打印信息, 17 | 从文件中读取,以及打开URL。 18 | 所有这些功能都是由底层执行环境提供的: 19 | 我们的操作系统。我们的操作系统提供了几个系统调用来支持IO、网络、文件系统和进程; 20 | Rust通过这些系统调用将这些功能委托给操作系统。 21 | 22 | 你可以翻阅std的模块,并尝试识别哪些模块依赖于操作系统。 23 | 当然,如果我们没有一个提供底层实现的操作系统,我们就不能使用这些功能。 24 | 一个叫做`no-std`的功能就是用于这些裸机环境。 25 | 在`no-std Rust`中,我们只能使用不依赖操作系统的核心特性。 26 | 看看`core crate`;`core crate`是`std crate`的一个子集; 27 | 你可以在核心中找到许多熟悉的模块,实现内存操作、算术或常用的类型结构。 28 | 29 | std和no-std之间的差别很小: 30 | 31 | - 在no-std中,你不能使用std crate,但是,你可以从核心中导入大部分模块。 32 | - 你不能使用与堆相关的模块(box, collections, string, etc.),因为默认的Rust内存分配器依赖于操作系统的系统调用来增加堆内存;除非你实现自己的全局分配器。 33 | - 如果你写一个bin crate,你必须实现一些lang项。 34 | 35 | 不要被这些陌生的术语吓到; 36 | 要理解这些,你需要知道一些rustc的概念,比如lang item或global allocator, 37 | 这些概念在std环境中被我们隐藏起来了。 38 | 39 | ## 什么是lang item? 40 | 41 | 简而言之:Rustc被设计成可插拔的; 42 | Rustc允许用户通过lang项来定制语言功能,而不是在编译器中内置所有操作。 43 | 44 | 长话短说:语言项文件 45 | 46 | 大部分的语言项都是在核心箱中定义的;但是,有些是在std箱中定义的。 47 | 例如,eh_personality是由失败机制使用的。 48 | 如果你在写一个无std的bin crate,你需要实现这些lang项以使编译器工作; 49 | 但如果你在写一个lib crate,你可以假设bin crate定义了这些lang项, 50 | 所以你不需要这样做。 51 | 52 | `lang`项的功能是不稳定的,这意味着我们只能在夜间的Rust中定义lang项。 53 | Rust团队通过编译器属性公开了一些lang项; 54 | 它允许我们在稳定的Rust中定义它们, 55 | 例如:`#[panic_handler]`定义了`panic_impl lang`项,而`#[alloc_error_handle]`定义了`oom lang`项。 56 | 57 | 一个建议是,在你试图从头开始实现它们之前,你应该始终寻找一个运行时支持板块。Rust嵌入式工作小组是一个很好的开始。他们提供了为不同的嵌入式环境定义lang项的板条;通过使用这些板条,你可以忘记lang项,获得更好的生活。 58 | 59 | ## 什么是alloc crate?什么是全局分配器? 60 | 61 | `alloc crate`包含与堆相关的模块; 62 | `alloc`中的模块使用全局分配器来分配内存。 63 | `std crate`定义了一个默认的全局分配器,它取决于操作系统; 64 | 当堆内存耗尽时,`std`全局分配器会调用操作系统的系统调用来增加内存。 65 | 所以在`no-std`环境下,我们需要定义我们的全局分配器; 66 | 我们可以使用`#[global_allocator]`属性来定义它。 67 | 通常情况下,我们使用一个固定的内存范围作为我们的堆; 68 | 当堆用尽时,我们不会调用`brk`或`mmap` 69 | (Linux系统调用,向操作系统索取更多的内存), 70 | 而是引发一个内存不足的错误。 71 | 72 | 有许多全局分配器的实现; 73 | 例如,最简单的是以链表的形式实现的; 74 | 这里是我使用好友分配器算法写的一个, 75 | 它可以保证在不同情况下的稳定响应时间。 76 | 77 | 通过定义全局分配器,我们可以在`no-std`程序中使用`alloc crate`。 78 | `alloc`包含了非常频繁使用的模块,如`string`、`box`、`collections` etc。 79 | `core`和`alloc crate`几乎涵盖了我在`std`中最经常使用的模块。 80 | 81 | ## 如何编写no-std lib crate 82 | 83 | 通过在`lib.rs`的顶部添加`#![no_std]`, 84 | 我们告诉`rustc`在`no-std Rust`下编译整个crate; 85 | 如果我们试图从std导入或使用依赖std的crate, 86 | 编译器会引发错误。通常,我们使用另一个编译条件 87 | `#![cfg_attr(not(test), no_std)]`来告诉 `rustc ` 88 | 只在测试标志被禁用时才编译到`no-std Rust`, 89 | 这样我们就可以在测试中使用`std`,就像 std Rust 那样。 90 | 91 | 如果我们需要使用 `alloc crate`, 92 | 我们需要在 `lib.rs` 的 `extern crate alloc` 中再添加一行; 93 | 因为 `alloc` 是一个内置的 `crate`,所以 `rustc` 会自动为我们链接它。 94 | 95 | ```rust 96 | //! lib.rs 97 | #![cfg_attr(not(test), no_std)] 98 | 99 | /// Add this line if you need to use alloc modules 100 | extern crate alloc; 101 | ``` 102 | 103 | ## 如何在我的crate中同时支持std和no-std环境? 104 | 习惯性的方法是使用[货物特性](https://doc.rust-lang.org/cargo/reference/features.html#features) 105 | 106 | 我们在Cargo.toml中添加一个`std`特性,并将其作为默认设置。 107 | 108 | ```toml 109 | # Cargo.toml 110 | [features] 111 | default = ["std"] 112 | std = [] 113 | ``` 114 | 115 | 然后在lib.rs中我们使用std特性作为编译条件。 116 | 117 | ```rust 118 | //! lib.rs 119 | #![cfg_attr(not(feature = "std"), no_std)] 120 | 121 | /// different implementations under std or no-std 122 | 123 | #[cfg_attr(feature = "std")] 124 | fn a () { // std implementation } 125 | 126 | #[cfg_attr(not(feature = "std"))] 127 | fn a () { // no-std implementation } 128 | ``` 129 | 130 | 因为我们将`std`定义为默认特性,所以我们的测试仍然在`std Rust`中进行编译。 131 | 132 | 我们也可以控制依赖性来启用`std`特性。 133 | 134 | ```toml 135 | # Cargo.toml 136 | [features] 137 | default = ["std"] 138 | std = ["crate-a/std", "crate-b/std"] 139 | 140 | [dependencies] 141 | crate-a = { version = "0.1", default-features = false } 142 | crate-b = { version = "0.1", default-features = false } 143 | ``` 144 | -------------------------------------------------------------------------------- /use-no-std/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "use-no-std" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | create-no-std-lib-1 = { path = "../create-no-std-lib-1" } 10 | create-no-std-lib-2 = { path = "../create-no-std-lib-2", default-features = false } 11 | -------------------------------------------------------------------------------- /use-no-std/src/main.rs: -------------------------------------------------------------------------------- 1 | use create_no_std_lib_1::get_hello_string; 2 | use create_no_std_lib_2::hello; 3 | 4 | fn main() { 5 | // test no-std create no std lib 1 6 | let temp = get_hello_string("hello"); 7 | println!("temp = {}",temp); 8 | 9 | let hello = hello("hello world"); 10 | println!("hello = {:?}", hello); 11 | 12 | 13 | } 14 | --------------------------------------------------------------------------------