LoginSignup
59
23

More than 3 years have passed since last update.

[Rust] フィーチャーフラグの使い方

Last updated at Posted at 2020-03-07

Rust ではコンパイル時にクレートの特定の機能の有効/無効を切り替えることのできる フィーチャーフラグ (feature flag) と呼ばれる機能があり, うまく使えばとても便利です. この記事でその使い方をまとめます.

クレート利用者向け

自分が依存したいクレートがフィーチャーフラグを持つとき, Cargo.toml で指定することによってそれを有効化します. 例えば rand クレートを serde1 フラグを立てた状態で使用したい場合は

Cargo.toml
[dependencies]
rand = { version = "0.7", features=["serde1"] }

となります. クレートによってはデフォルトでいくつかのフラグが有効になっていて, しかしそれを無効化したいということがあります. その場合は default-features=false を指定します.

クレート作成者向け

以下がこの記事の主題です. 自クレートでフィーチャーフラグを立てる方法を説明します.

フィーチャーフラグの定義

あるクレートで独自にフィーチャーフラグを設定する場合, Cargo.tomlfeatures セクションを設けてその中で定義します. 例えば:

Cargo.toml
[features]
default = []
japanese = []

default でデフォルトで有効なフィーチャーフラグをまとめて設定します (なくても構いません). ともかく, これでフィーチャーフラグ japanese が使えるようになったので, Rust コードの中で cfg アトリビュートを用いてフラグの有無で関数の定義などを分岐します.

src/lib.rs
#[cfg(not(feature="japanese"))]
pub fn hello() -> &'static str {
    "hello"
}
#[cfg(feature="japanese")]
pub fn hello() -> &'static str {
    "こんにちは"
}

#[cfg(test)]
mod tests {
    use crate::*;

    #[test]
    fn it_works() {
        assert_eq!( hello(), "hello" );
    }
}

見ての通り, 関数 hello は, フラグ japanese が立っていないときには "hello" を, 立っているときには "こんにちは" を返します. なおこの例では関数定義に cfg アトリビュートを付与しましたが, cfg! マクロを使えば同等の実装をより簡潔に書くことができます.

src/lib.rs
pub fn hello() -> &'static str {
    if cfg!(feature="japanese") {
        "こんにちは"
    } else {
        "hello"
    }
}

#[cfg(test)]
// 以下省略 //

しかし, フラグが立っているときにのみある関数を定義したいという状況ではアトリビュートを使うことになります.

さて, 上のコードに含まれるテストはフラグ japanese が立っていないときには成功し, 立っていれば失敗するでしょう. 普通に cargo test を実行すると, デフォルトではフラグを立てていないため, テストをパスします. が, --features オプションを通じてフラグを立ててコンパイルすれば失敗するようにできます.

$ cargo test
   Compiling a v0.1.0 (/home/osanshouo/misc/featureflag/a)
    Finished test [unoptimized + debuginfo] target(s) in 0.68s
     Running target/debug/deps/a-2627f60e1e33f0a2

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

$
$
$ cargo test --features japanese
   Compiling a v0.1.0 (/home/osanshouo/misc/featureflag/a)
    Finished test [unoptimized + debuginfo] target(s) in 0.71s
     Running target/debug/deps/a-48b8540e44e9729b

running 1 test
test tests::it_works ... FAILED

failures:

---- tests::it_works stdout ----
thread 'tests::it_works' panicked at 'assertion failed: `(left == right)`
  left: `"こんにちは"`,
 right: `"hello"`', src/lib.rs:26:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.


failures:
    tests::it_works

test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out

error: test failed, to rerun pass '--lib'

外部クレートの制御

外部クレート (例えば rand) に optional で依存しているとき, 自動的にそのクレート名 (rand) というフィーチャーフラグが設定されます. そして, そのフラグが立っているときにのみその外部クレートをビルドします. 例えば次の例ではデフォルトでは rand はビルドされませんが, japanese フラグが立っているときには rand フラグも立ち, ビルドされます.

Cargo.toml
[features]
default = []
japanese = [ "rand" ]

[dependencies]
rand = { version = "0.7", optional = true }

この振る舞いのために, フィーチャーフラグの名称としては自分が依存しているクレートの名前を使うことはできません. なお自分がいま開発しているクレートからは, 依存クレートが持つフィーチャーフラグは (クレート名)/(フィーチャー名) という形で見えています. なのでフィーチャー名がブッキングしてもそれで意図せず依存クレートのフラグが立つということはありません.

この性質を利用して, 依存クレートのフィーチャーフラグを自クレートのフィーチャーフラグを通じて制御することができます. 例えば rand クレートの serde1 フラグを自クレートの japanese フラグが立っているときにのみ有効化するには

Cargo.toml
[features]
default = []
japanese = [ "rand/serde1", ]

[dependencies]
rand = "0.7"

とします.

参考文献

59
23
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
59
23