LoginSignup
18
5

More than 3 years have passed since last update.

既存のクレートを改造して使う

Last updated at Posted at 2020-12-16

この記事は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

Playground で実行する

このコードで得られる文字列は a-z, A-Z, 0-9 の範囲から文字がランダムに選ばれます。ここで、数字 0-9 が選ばれるのが気に入らなかったとしましょう。a-z, A-Z のみの範囲から文字が選ばれるようにしたいと思います。

STEP1: rand クレートのリポジトリを fork して編集する

まずは rand クレートのリポジトリ を fork して改造します。このあたりのコード を書き換えればよさそうですね

src/distributions/other.rs

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 クレートのリポジトリを記載します

Cargo.toml
[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 を適当に編集しましょう

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

18
5
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
18
5