この記事はRust 2 Advent Calendar 2020 の16日目の記事です。
はじめに
crates.io では様々なクレートが配布されています。Rust でコードを書いていると、既存のクレートの機能がイマイチで動作を書き換えたくなることがあります。このようなときは、cargo の patch 機能を使うと便利です。
この記事の内容は以下の環境で検証しました:
stable-x86_64-apple-darwin (default)
rustc 1.48.0 (7eac88abb 2020-11-16)
rand クレートの動作を書き換える
乱数を生成するクレート、 rand の動作を書き換えてみましょう。
rand では確率分布を指定することができるのですが、rand::distributions::Alphanumeric
を使うとランダムな ASCII 文字列を生成することができます。例えば、このようなコードを書くと
use std::iter;
use rand::{Rng, thread_rng};
use rand::distributions::Alphanumeric;
fn main() {
let mut rng = thread_rng();
let chars: String = iter::repeat(())
.map(|()| rng.sample(Alphanumeric))
.take(7)
.collect();
println!("Random chars: {}", chars);
}
このような結果が得られます:
Random chars: rVZ27tb
このコードで得られる文字列は a-z, A-Z, 0-9
の範囲から文字がランダムに選ばれます。ここで、数字 0-9
が選ばれるのが気に入らなかったとしましょう。a-z, A-Z
のみの範囲から文字が選ばれるようにしたいと思います。
STEP1: rand クレートのリポジトリを fork して編集する
まずは rand クレートのリポジトリ を fork して改造します。このあたりのコード を書き換えればよさそうですね
impl Distribution<u8> for Alphanumeric {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> u8 {
- const RANGE: u32 = 26 + 26 + 10;
+ const RANGE: u32 = 26 + 26;
const GEN_ASCII_STR_CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
- abcdefghijklmnopqrstuvwxyz\
- 0123456789";
+ abcdefghijklmnopqrstuvwxyz";
// We can pick from 62 characters. This is so close to a power of 2, 64,
// that we can do better than `Uniform`. Use a simple bitshift and
// rejection sampling. We do not use a bitmask, because for small RNGs
// the most significant bits are usually of higher quality.
loop {
let var = rng.next_u32() >> (32 - 6);
if var < RANGE {
return GEN_ASCII_STR_CHARSET[var as usize];
}
}
}
}
crates.io で公開されている最新バージョンは 0.7.3
だったので、0.7.3
タグから patched
ブランチを生やし、コミットして push しておきました。できたものがこちらになります Kumassy/qiita-patched-rand
STEP2: 改造したクレートを使う
改造した rand クレートを利用するアプリケーションを適当に作ります。
$ cargo new --bin qiita-patch-crate
としてプロジェクトの雛形を作り、Cargo.toml を編集します。dependencies
セクションには通常通り rand
を記載しますが、patch.crates-io
セクションには改造した rand クレートのリポジトリを記載します
[package]
name = "qiita-patch-crate"
version = "0.1.0"
authors = ["Kumassy"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
rand = "0.7.3"
[patch.crates-io]
rand = { git = 'https://github.com/Kumassy/qiita-patched-rand', branch = 'patched' }
src/main.rs
を適当に編集しましょう
use std::iter;
use rand::{Rng, thread_rng};
use rand::distributions::Alphanumeric;
fn main() {
let mut rng = thread_rng();
let chars: String = iter::repeat(())
.map(|()| rng.sample(Alphanumeric))
.take(7)
.collect();
println!("Random chars: {}", chars);
}
これを実行すると、数字 0-9
を含まないランダムな ASCII 文字列が得られるようになります:
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.04s
Running `target/debug/qiita-patch-crate`
Random chars: "HRHKpIq
できたものはこちらにあります
Kumassy/qiita-patch-crate
まとめ
Cargo.toml の patch
セクションを使うことで、既存のクレートの動作を書き換えることができました。crates.io にまだ公開されてないバージョンのクレートを試したり、クレートのバグフィックスを検証したりするときにも便利そうですね。
もっと詳しく
The Rust Book に詳しい解説があるので、こちらもあわせてご覧ください
Overriding Dependencies
https://doc.rust-lang.org/cargo/reference/overriding-dependencies.html