July 08, 2020
English | 日本語
Rust の Crate で Features を使う (日本語)¶
Rust のライブラリを作っているとしよう。
"greetings" の feature を有効にしたときだけ関数 "hello()" を公開したい場合はどうすればよいだろうか?
普通に "hello()" を公開する crate¶
まずはローカルの crate と、その関数を使う実行ファイルを作ってみよう。
ライブラリ crate "foo"¶
Shell で以下のように実行。
# shell
$ cargo new --lib foo
次に foo/src/lib.rs を編集。
// foo/src/lib.rs
pub fn hello() {
println!("Hello world.");
}
"foo::hello()" を呼ぶ実行ファイル bar¶
Shell で以下のように実行
# shell
$ cargo new --bin bar
次に bar/src/main.rs を編集。
// bar/src/main.rs
extern crate foo;
fn main() {
foo::hello();
}
最後に bar/Cargo.toml の [dependencies] セクションに以下の行を追加。
# bar/Cargo.toml
...
[dependencies]
foo = { version = "^0", path = "../foo" }
...
Shell で実行すると "Hello world." と表示される。
# shell
$ (cd bar; cargo run)
"hello()" を "greetings" feature にする¶
"foo::hello()" が正常動作することを確認したら、これを feature にしてみる。
まず foo に feature を作る。
foo/Cargo.toml に "[features]" セクションをつくり "greetings" を追加。
# foo/Cargo.toml
...
[features]
greetings = []
...
(空の角カッコは、その feature が他の crate に依存していない事を示す。詳細は後述。)
次に "hello()" を "greetings" 特有の機能にする。 '[cfg(feature = "greetings")]' を foo/src/lib.rs に追加。
// foo/src/lib.rs
#[cfg(feature = "greetings")]
pub fn hello() {
println!("Hello world.");
}
この段階で bar のコンパイルが通らなくなるので bar/Cargo.toml の依存関係を修正する。
// bar/Cargo.toml
...
[dependencies]
foo = { version = "^0", path = "../foo", features = ["greetings"] }
...
長い行が気に入らなければ "[dependencies.foo]" セクションを作っても良い。
# bar/Cargo.toml
...
[dependencies.foo]
version = "^0"
path = "../foo"
features = ["greetings"]
...
bar のビルドが通るはず。
feature の依存関係¶
ところで、もし feature が他のクレートに依存していたらどうするか? 例えば "greetings" のみが libc に依存している場合を考えてみる。
foo/Cargo.toml に次のように書くと libc は常にダウンロードされてしまう。
# foo/Cargo.toml
...
[dependencies]
libc = "0.2"
...
これは良くない。 optional にするべき。
# foo/Cargo.toml
...
[dependencies]
libc = { version = "0.2", optional = true }
[featres]
greetings = ["libc"]
...
こすると libc は greetings が有効な時にだけダウンロードされる。
default features¶
特定の feature をデフォルトにしたい場合、default feature を作る事ができる。
foo/Cargo.toml を次のように編集。
# foo/Cargo.toml
...
[features]
default = ["greetings"]
greetings = ["libc"]
...
これで "greetings" はデフォルトになったので使う時に明記する必要がなくなった。 bar/Cargo.toml の依存関係は以下で十分。
# bar/Cargo.toml
...
[dependencies]
foo = { version = "^0", path = "../foo" }
...
もし、あえてデフォルトの features を無効にしたい場合、次のようにすれば良い。 (もちろん、ビルドは通らなくなる。)
# bar/Cargo.toml
...
[dependencies]
foo = { version = "^0", path = "../foo", default-features = false }
...
結論¶
個人的にはこの仕組みを気に入った。 依存関係が少ないほど、コンパイル時間やバグが減る。
Rust は C/C++ に比べて複雑だけど、よく考えられていると思う。 時間をかけて勉強する価値がある。