⚙️

Feature Flagを使用してコンパイルする機能を切り替える

2023/10/09に公開

概要

ライブラリクレートを作成する際に利用者が必要な機能だけをコンパイルしたり、コンパイルされる機能を切り替えたりすることができると便利です。これを実現するために、RustではFeature Flagという機能が用意されています。

何が便利なの?

実用例としては画像処理ライブラリを作成した時に、JPEGを処理する機能とPNGを処理する機能を切り替えることができるようにするといったことが挙げられます。また、実装によっては関数の内部処理すらも切り替えることができる為ユーザーニーズに合わせた最適なライブラリを提供することができます。

設定方法

以下2点を設定することでFeature Flagを利用することができます。

  1. Cargo.tomlでFeature Flagを定義する
  2. #[cfg(feature = "feature_name")]でFeature Flagの対象を指定する

Cargo.toml

[package]
name = "mylib"
version = "0.1.0"
edition = "2021"

[features]
default = ['basic']
basic = []
extra = []

この場合、extrabasicというFeature Flagが定義されます。defaultは特別なFeature Flagで、cargo buildなどのコマンドを実行した際にデフォルトで有効になるFeature Flagを指定します。上記の例ではbasicがデフォルトで有効になります。

#[cfg(feature = "feature_name")]

#[cfg(feature = "basic")]
pub fn basic() {
    println!("basic");
}

#[cfg(feature = "extra")]
pub fn extra() {
    println!("extra");
}

#[cfg(not(feature = "extra"))]
pub fn not_extra() {
    println!("not extra");
}

#[cfg(not(any(feature = "basic", feature = "extra")))]
pub fn very_basic() {
    println!("very basic");
}

解説

上記の例では4つの関数がFeature Flagによってコンパイルされるかどうかが切り替わります。

  1. basic関数はbasicが有効な場合のみコンパイルされます、今回はbasicがデフォルトで有効なので未指定でもコンパイルされます。
  2. extra関数はextraが有効な場合のみコンパイルされます。未指定ではコンパイルされません。
  3. not_extra関数はextraが無効な場合のみコンパイルされます。未指定=extraが無効なのでコンパイルされます。
  4. very_basic関数はbasicextraが無効な場合のみコンパイルされます。未指定=basicが有効なのでコンパイルされず、basicを明示的に無効にするとコンパイルされます。

利用方法

実装したライブラリを使用する場合は、呼び出し側のCargo.tomlに以下のように記述します。

[dependencies]
mylib = { version = "0.1.0", features = ["extra"] }

この場合、デフォルトとextraが有効になります。

デフォルトを無効にする場合は以下のように記述します。

[dependencies]
mylib = { version = "0.1.0", default-features = false, features = ["extra"] }

この場合デフォルトに含まれる全てのFeature Flagが無効になります(今回はbasic)。

ちなみに現時点ではデフォルトの一部のみを無効化することはできないようです。
https://github.com/rust-lang/cargo/issues/3126

おまけ

難しい話ですが、あるFeatureが外部クレートのFeatureに依存している場合、外部クレートのFeatureが有効になっていないとコンパイルエラーになります。なので、Feature Flagを利用する際は依存関係を記述することでコンパイル時に自動設定してくれます。

[features]
default = ['basic']
basic = []
extra = ['rand/serde-1']

[dependencies]
rand = { version = "0.8"}
// rand = { version = "0.8", features = ["serde-1"]} extra時はこれと等しくなる

Discussion