banner
davirain

davirain

twitter
github
知乎
twitter

Rust no-std プロジェクト実践

改写 std のライブラリを no_std をサポートするライブラリにし、std と no_std の両方をサポートするライブラリの経験談を記述した github リポジトリ: https://github.com/DaviRain-Su/rust-no-std-source

概要#

まず、std と no_std の違いを紹介し、次に no_std ライブラリの使用方法を説明します。no_std をサポートする特性には 2 つの異なる方法があるため、no_std ライブラリの使用方法も 2 つあります。次に、ライブラリが no_std 特性をサポートしているかどうかを検証する方法、std のライブラリを std と no_std の両方をサポートするように改写する方法について説明します。具体的に、std と no_std をサポートするライブラリの書き方、一部の std と no_std の両方で使用できるプリミティブのリポジトリや関連リソース、記事についても触れます。

目次#

  • std と no_std の違い
  • Rust における no_std の 2 つの使用方法
  • ライブラリが no_std 特性をサポートしているかどうかの検証方法
  • std と no_std をサポートするライブラリの具体的な書き方
  • no_std と std で使用できるプリミティブ型のリポジトリや関連リソースの記事

std と no_std の違い#

コアライブラリ#

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 モジュールはプログラムの引数、環境変数、ディレクトリナビゲーションなど、操作環境と相互作用する基本機能を提供します;path モジュールはファイルパスを処理するプラットフォーム固有のルールをカプセル化します。
  • 基本的な操作インターフェース、例えば std::mem、std::ptr、std::intrinsics など、メモリ、ポインタ、コンパイラ固有関数を操作します。
  • オプションとエラー処理型 Option と Result、さまざまなイテレータなど。

さらに説明すると、#![no_std]は crate レベルの属性であり、core crate が std crate ではなく core crate にリンクされることを示します。

以下は std crate と core crate の説明であり、実際には標準ライブラリとコアライブラリの違いを説明しています。また、std と no_std の違いも内在的に含まれています。

まず、std crate は Rust の標準ライブラリです。含まれる機能は、プログラムがオペレーティングシステム上で実行されることを前提としています。std は、オペレーティングシステムが一般的なものであると仮定しており、サーバーやデスクトップで見られるようなものです。このため、std は通常この種のオペレーティングシステムで見られる機能に対して標準 API を提供します:スレッド、ファイル、ソケット、ファイルシステム、プロセスなど。

次に、core crate は std crate のサブセットであり、プログラムが実行されるシステムについて何の仮定も行いません。したがって、浮動小数点、文字列、スライスなどの言語ベースの API や、原子操作や SIMD 命令などのプロセッサ特性を扱う API を提供します。しかし、ヒープメモリの割り当てや I/O に関する API は欠けています。

アプリケーションにとって、std が提供するのはオペレーティングシステムの抽象へのアクセス方法だけではなく、スタックオーバーフロー保護、コマンドライン引数の処理、プログラムのメイン関数が呼び出される前にメインスレッドを生成することも含まれます。#![no_std]アプリケーションは、これらの標準的なランタイムを欠いているため、自分自身のランタイムを初期化する必要があります。

これらの特性により、#![no_std]アプリケーションはシステム上で最初または唯一の実行コードになる可能性があります。

Rust における no_std のいくつかの使用方法#

主に no_std の第 2 の使用方法を具体的に紹介します。

具体的な使用方法については、no_std ライブラリの第 2 の使用方法を参照してください。

また、参考として、インスタンス:serde no-std の使用規範

ライブラリが no_std 特性をサポートしているかどうかの検証方法#

cargo check --target wasm32-unknown-unknown

ただし、wasm 環境が必ずしも no_std であるとは限らず、他のコンパイルターゲットも可能です。つまり、裸のコンパイルターゲット環境にはシステム環境が含まれていません。

参考文献: Rust でオペレーティングシステムを書く(一):独立した実行可能プログラム

no_std ライブラリの具体的な書き方#

no_std ライブラリを作成する第 1 の方法(#![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 ライブラリの第 2 の作成方法(#![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 で使用できるプリミティブ型のリポジトリ#

引用およびリソース#

結論#

serde の使用やいくつかのフォーラムの議論を参考にして、#![cfg_attr(not(feature = "std"), no_std)]を使用して std と no_std の両方をサポートすることを推奨します。

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。