LoginSignup
7
2

More than 3 years have passed since last update.

【Rust】crates.io被依存数ランキング

Posted at

crates.ioはRustのライブラリ(クレート)管理の中心であり、cargoコマンドによる依存クレートのダウンロードも特に指定がない限りはこのサイトの登録をもとに行われる。このサイトはGitHubのアカウントがあれば誰でも新しいクレートを登録できるため、登録されているクレートはほぼ標準に近いものから個人の実験的なものまで様々なレベルのものが混在している。

機能が安定したクレートを探す際はカテゴリーキーワードで絞り込んでダウンロード数が多いものから選択すれば基本的に問題ないが、今回は別の指標として被依存数(他のクレートからの依存の数)を集計してみた。

集計内容

以下の3項目を集計する。

  • 直接被依存数:そのクレートに[dependencies]で直接依存するクレートの数
  • 完全被依存数:そのクレートに[dependencies]で直接または間接的に依存するクレートの数
  • 開発被依存数:そのクレートに[build-dependencies]または[dev-dependencies]で直接依存するクレートの数

直接被依存数の上位は、機能の粒度などが汎用的で扱いやすいクレートが多くなると予想される。

完全被依存数の上位は、低レベルの機能を提供するクレートが多くなると予想される。また、間接的な依存を含めるため

AがBに依存する
↓
{Aに依存するクレート} ⊂ {Bに依存するクレート}
↓
Aの完全被依存数 < Bの完全被依存数

となり、必然的に上位は他のクレートへの依存が少ないものが多くなる。

開発被依存数の上位は、テストコードで使用されるクレートなどが多くなると予想される。

注意事項

依存指定の中には特定の条件下でのみ必要なoptionalな依存も存在するが、今回は通常の依存と区別せずに集計した。

また、依存対象のバージョン(の上限)が指定される場合があるが、今回の集計ではそれらを無視し、すべて最新バージョンに依存するものとしている。これは以下のような間接的な依存を扱う際に問題となる。

  • AはB(1.0.0)に依存する
  • B(1.0.0)はCに依存しない
  • B(2.0.0)(最新バージョン)はCに依存する

この場合は「AはCに依存しない」とするのが正しいが、今回の集計では

  • AはB(最新バージョン)に依存する
  • B(最新バージョン)はCに依存する

とみなし、「AはCに依存する」ものとしてカウントする。この辺をまじめに考えると話がややこしくなるので、誤差として許容願いたい。

集計方法

データ取得

crates.ioのデータを取得する方法は、同サイト内のAccessing the Crates.io Dataで紹介されている。今回の集計では2番目のstatic.crates.io/db-dump.tar.gzを使用した。このファイルはcrates.io内部のデータベースを出力したCSVで構成され、24時間ごとに更新されている。データの内容はcrates.ioのソースコードを見て確認する必要があるが、ある程度はCSVの項目名からも推測できる。

データ処理

集計は下記のPythonスクリプトで行った。コマンドライン引数に上記のデータファイルを渡すと、実行したディレクトリにクレートごとの各集計値をもつCSVファイルを出力する。筆者はPython 3.8.3(Linux)で実行したが、多少古いバージョンでも動くと思う。

$ python3.8 count_deps.py db-dump.tar.gz 
# count_deps.py
import csv
import datetime
import re
import sys
import tarfile

def main():
    # relax csv field size limit (default value makes failure)
    csv.field_size_limit(sys.maxsize)

    if len(sys.argv) < 2:
        print("no arguments", file=sys.stderr)
        sys.exit(1)
    file_path = sys.argv[1]

    print("extracting tar ...")
    with tarfile.open(file_path, "r:*") as tar:
        for member in tar.getmembers():
            name = member.name.rstrip('/')
            if '/' not in name:
                date_label = name[:10]
            elif name.endswith("/data/crates.csv"):
                crates_csv = member
            elif name.endswith("/data/categories.csv"):
                categories_csv = member
            elif name.endswith("/data/crates_categories.csv"):
                crates_categories_csv = member
            elif name.endswith("/data/versions.csv"):
                versions_csv = member
            elif name.endswith("/data/dependencies.csv"):
                dependencies_csv = member

        print("loading crates ...")
        crates = collect_crates(tar.extractfile(crates_csv))
        print("loading categories ...")
        categories = collect_categories(tar.extractfile(categories_csv))
        bind_categories(crates, categories,
                        tar.extractfile(crates_categories_csv))
        print("loading versions ...")
        bind_latest_version(crates, tar.extractfile(versions_csv))
        verid_to_crate = {}
        for crate in crates.values():
            verid_to_crate[crate.version.id] = crate
        print("loading dependencies ...")
        bind_users(crates, verid_to_crate,
                   tar.extractfile(dependencies_csv))

    print("resolving indirect dependencies ...")
    progress_tab = [(len(crates) * n // 10 - 1, n * 10) for n in range(1, 11)]
    for n, crate in enumerate(crates.values()):
        all_users = crate.all_users
        rest = list(crate.dir_users)
        while rest:
            dir_user = rest.pop(0)
            all_users.add(dir_user)
            rest.extend(
                x for x in dir_user.dir_users if x not in all_users and x != crate)

        if n == progress_tab[0][0]:
            _, percent = progress_tab.pop(0)
            print("  {}%".format(percent))

    output_csv = "crates_" + date_label + ".csv"
    print("writing {} ...".format(output_csv))
    write_crates_csv(crates, output_csv)

def as_bool(s):
    return {'t': True, 'f': False}[s]

def as_datetime(s):
    return datetime.datetime.strptime(s[:19], "%Y-%m-%d %H:%M:%S")

# re_semver ignores build metadata
re_semver = re.compile(r"(\d+\.\d+\.\d+)(\-([0-9A-Za-z\.\-]+))?")

def as_semver(s):
    m = re_semver.match(s)
    triple, pre = m.group(1, 3)
    # (major, minor, patch)
    triple = tuple(map(int, triple.split('.')))
    if pre:
        # map numeric to (0, int) and nonnumeric to (1, str) for rules:
        # - numeric < non-numeric
        # - numeric is compared as number
        # - non-numeric is compared alphabetically
        # e.g. alpha.2.11 -> ((1, 'alpha'), (0, 2), (0, 11))
        def pre_part_to_pair(part):
            return (0, int(part)) if part.isdigit() else (1, part)
        pre = tuple(map(pre_part_to_pair, pre.split('.')))

    return triple, pre

class Crate:
    __slots__ = [
        'id', 'name', 'categories', 'desc', 'created_at', 'updated_at',
        'version', 'dir_users', 'dev_users', 'all_users',
    ]

class Version:
    __slots__ = [
        'id', 'num', 'triple', 'pre', 'yanked',
    ]

    def preferred_to(self, other):
        if self.yanked != other.yanked:
            return not self.yanked
        elif bool(self.pre) != bool(other.pre):
            return not self.pre
        elif self.triple != other.triple:
            return self.triple > other.triple
        else:
            return self.pre and other.pre and self.pre > other.pre

def collect_crates(csv_file):
    crates = {}
    reader = csv.DictReader(map(lambda b: b.decode('utf-8'), csv_file))

    for row in reader:
        crate = Crate()
        crate.id = int(row['id'])
        crate.name = row['name']
        crate.categories = []
        crate.desc = row['description']
        crate.created_at = as_datetime(row['created_at'])
        crate.updated_at = as_datetime(row['updated_at'])
        crate.version = None
        crate.dir_users = set()
        crate.dev_users = set()
        crate.all_users = set()

        crates[crate.id] = crate

    return crates

def collect_categories(csv_file):
    categories = {}
    reader = csv.DictReader(map(lambda b: b.decode('utf-8'), csv_file))

    for row in reader:
        id_ = int(row['id'])
        category = row['category']
        categories[id_] = category

    return categories

def bind_categories(crates, categories, csv_file):
    reader = csv.DictReader(map(lambda b: b.decode('utf-8'), csv_file))

    for row in reader:
        crate = crates[int(row['crate_id'])]
        category = categories[int(row['category_id'])]
        crate.categories.append(category)

def bind_latest_version(crates, csv_file):
    reader = csv.DictReader(map(lambda b: b.decode('utf-8'), csv_file))

    for row in reader:
        version = Version()
        version.id = int(row['id'])
        version.num = row['num']
        version.triple, version.pre = as_semver(version.num)
        version.yanked = as_bool(row['yanked'])

        crate_id = int(row['crate_id'])
        crate = crates[crate_id]
        if crate.version is None or version.preferred_to(crate.version):
            crate.version = version

def bind_users(crates, verid_to_crate, csv_file):
    reader = csv.DictReader(map(lambda b: b.decode('utf-8'), csv_file))
    for row in reader:
        verid = int(row['version_id'])
        source_crate = verid_to_crate.get(verid, None)
        if not source_crate:
            continue
        target_crate = crates[int(row['crate_id'])]
        kind = row['kind']
        # 0: dependencies
        # 1: build_dependencies
        # 2: dev_dependencies
        if kind == '0':
            target_crate.dir_users.add(source_crate)
        elif kind in ('1', '2'):
            target_crate.dev_users.add(source_crate)

def write_crates_csv(crates, csv_path):
    fieldnames = [
        "name", "version", "created_at", "updated_at", "categories", "description",
        "dir_users_count", "all_users_count", "dev_users_count",
    ]
    with open(csv_path, 'w', encoding='utf-8') as csv_file:
        writer = csv.DictWriter(csv_file, fieldnames)
        writer.writeheader()

        for crate in crates.values():
            row = {
                'name': crate.name,
                'version': crate.version.num,
                'created_at': crate.created_at,
                'updated_at': crate.updated_at,
                'categories': " | ".join(sorted(crate.categories)),
                'description': crate.desc,
                'dir_users_count': len(crate.dir_users),
                'all_users_count': len(crate.all_users),
                'dev_users_count': len(crate.dev_users),
            }
            writer.writerow(row)

if __name__ == '__main__':
    main()

本当はRustで実装したかったが、tar.gzを直接読めるクレートが見当たらなかった(flate2+tarで試したが遅すぎて断念)。

集計結果

以下は2020-10-08時点のデータを集計した各被依存数の50位までの一覧である。名前の部分はcrates.ioのリンクになっている。

直接被依存数

順位 名前
バージョン
作成日
更新日
カテゴリー 被依存数
1 serde
1.0.116
2014-12-05
2020-09-11
Encoding 9190
2 serde_json
1.0.58
2015-08-07
2020-09-30
Encoding 5656
3 log
0.4.11
2014-12-13
2020-07-16
Development tools::Debugging 5518
4 serde_derive
1.0.116
2016-08-29
2020-09-11
- 3785
5 clap
2.33.3
2015-03-01
2020-09-19
Command-line interface 3607
6 libc
0.2.79
2015-01-15
2020-10-05
External FFI bindings
No standard library
Operating systems
3399
7 lazy_static
1.4.0
2014-11-23
2020-01-13
Memory management
No standard library
Rust patterns
3315
8 rand
0.7.3
2015-02-03
2020-01-10
Algorithms
No standard library
3130
9 futures
0.3.6
2016-07-31
2020-10-05
Asynchronous 2933
10 chrono
0.4.19
2014-11-20
2020-09-30
Date and time 2641
11 regex
1.3.9
2014-12-13
2020-05-29
Text processing 2540
12 syn
1.0.42
2016-09-07
2020-09-26
Development tools::Procedural macro helpers 2059
13 quote
1.0.7
2016-09-03
2020-06-07
Development tools::Procedural macro helpers 2048
14 failure
0.1.8
2017-10-08
2020-05-02
- 2021
15 byteorder
1.3.4
2015-02-03
2020-02-07
Encoding
Parsing tools
1807
16 tokio
0.2.22
2016-07-01
2020-07-22
Asynchronous
Network programming
1805
17 reqwest
0.10.8
2016-10-16
2020-08-25
Web programming::HTTP client
WebAssembly
1670
18 url
2.1.1
2014-11-14
2020-01-09
Encoding
Parser implementations
Web programming
1624
19 hyper
0.13.8
2014-11-22
2020-09-18
Network programming
Web programming::HTTP client
Web programming::HTTP server
1613
20 proc-macro2
1.0.24
2017-07-06
2020-10-01
Development tools::Procedural macro helpers 1597
21 structopt
0.3.19
2017-02-03
2020-10-08
Command-line interface 1589
22 bytes
0.5.6
2015-01-30
2020-07-14
Data structures
Network programming
1388
23 thiserror
1.0.21
2019-10-09
2020-10-06
Rust patterns 1342
24 env_logger
0.7.1
2015-01-27
2019-10-17
Development tools::Debugging 1244
25 toml
0.5.6
2014-11-11
2020-01-16
Configuration
Encoding
Parser implementations
1213
26 winapi
0.3.9
2014-11-29
2020-06-26
External FFI bindings
No standard library
Operating systems::Windows APIs
1160
27 anyhow
1.0.33
2019-10-05
2020-10-06
Rust patterns 1146
28 num-traits
0.2.12
2016-04-14
2020-06-11
Algorithms
No standard library
Science
1088
29 base64
0.13.0
2015-12-04
2020-09-30
Encoding 1068
30 bitflags
1.2.1
2015-01-15
2019-10-13
No standard library 1042
31 itertools
0.9.0
2014-11-21
2020-03-07
Algorithms
Rust patterns
986
32 uuid
0.8.1
2014-11-11
2019-10-18
Data structures
No standard library
Parser implementations
WebAssembly
864
33 time
0.2.22
2014-11-13
2020-09-26
Date and time 847
34 mime
0.3.16
2014-11-20
2020-01-07
- 775
35 async-trait
0.1.41
2019-07-23
2020-10-01
- 739
36 rayon
1.4.1
2015-12-10
2020-09-29
Concurrency 716
37 nom
5.1.2
2015-01-17
2020-10-04
Parsing tools 709
38 dirs
3.0.1
2015-11-24
2020-07-04
- 683
39 error-chain
0.12.4
2016-04-29
2020-08-02
Rust patterns 671
40 num
0.3.0
2014-11-11
2020-06-13
Algorithms
Data structures
No standard library
Science
633
40 hex
0.4.2
2015-10-23
2020-02-18
Encoding
No standard library
633
42 rustc-serialize
0.3.24
2014-12-11
2017-04-21
- 579
43 bincode
1.3.1
2014-11-20
2020-06-24
Encoding
Network programming
567
44 sha2
0.9.1
2016-05-06
2020-06-24
Cryptography
No standard library
550
45 http
0.2.1
2014-11-20
2020-03-25
Web programming 545
46 colored
2.0.0
2016-03-13
2020-07-14
- 538
47 image
0.23.10
2014-11-20
2020-09-26
Multimedia::Encoding
Multimedia::Images
537
48 yup-oauth2
4.1.2
2015-02-27
2020-05-01
- 535
49 parking_lot
0.11.0
2016-05-13
2020-06-23
Concurrency 532
50 serde_yaml
0.8.13
2016-02-27
2020-06-10
- 513

完全被依存数

順位 名前
バージョン
作成日
更新日
カテゴリー 被依存数
1 unicode-xid
0.2.1
2015-04-27
2020-06-24
- 29444
2 proc-macro2
1.0.24
2017-07-06
2020-10-01
Development tools::Procedural macro helpers 29423
3 quote
1.0.7
2016-09-03
2020-06-07
Development tools::Procedural macro helpers 29397
4 syn
1.0.42
2016-09-07
2020-09-26
Development tools::Procedural macro helpers 29336
5 rustc-std-workspace-core
1.0.0
2018-11-21
2018-11-21
- 28214
6 libc
0.2.79
2015-01-15
2020-10-05
External FFI bindings
No standard library
Operating systems
27513
7 serde_derive
1.0.116
2016-08-29
2020-09-11
- 27112
8 serde
1.0.116
2014-12-05
2020-09-11
Encoding 27110
9 compiler_builtins
0.1.36
2018-11-21
2020-09-13
- 25464
10 spin
0.5.2
2015-01-27
2019-08-27
- 24971
11 lazy_static
1.4.0
2014-11-23
2020-01-13
Memory management
No standard library
Rust patterns
24929
12 cfg-if
1.0.0
2015-07-08
2020-10-06
- 24609
13 hermit-abi
0.1.17
2019-10-20
2020-10-06
Operating systems 23533
14 smallvec
1.4.2
2015-04-06
2020-08-11
Data structures 23466
15 sval_derive
0.5.2
2018-12-03
2020-08-20
- 23345
16 sval
0.5.2
2018-12-03
2020-08-20
Encoding
No standard library
23344
17 log
0.4.11
2014-12-13
2020-07-16
Development tools::Debugging 23341
18 either
1.6.1
2015-09-14
2020-09-16
Data structures
No standard library
22867
19 no-panic
0.1.14
2018-08-03
2020-06-16
- 22794
20 ryu
1.0.5
2018-07-28
2020-05-31
- 22784
21 crossbeam-utils
0.7.2
2017-08-28
2020-02-22
Algorithms
Concurrency
Data structures
No standard library
22710
22 maybe-uninit
2.0.0
2019-07-09
2019-07-10
- 22694
23 num_cpus
1.13.0
2015-03-16
2020-04-15
Hardware support 22675
24 scopeguard
1.1.0
2015-04-30
2020-02-16
No standard library
Rust patterns
22665
25 crossbeam-channel
0.4.4
2017-11-26
2020-09-06
Algorithms
Concurrency
Data structures
22645
26 memoffset
0.5.6
2017-10-17
2020-09-20
No standard library 22636
27 crossbeam-epoch
0.8.2
2017-11-26
2020-02-22
Concurrency
Memory management
No standard library
22621
28 crossbeam-deque
0.7.3
2017-11-26
2020-02-22
Algorithms
Concurrency
Data structures
22610
29 proc-macro-hack
0.5.18
2016-12-30
2020-07-26
Development tools::Procedural macro helpers 22601
30 rayon-core
1.8.1
2017-03-11
2020-09-17
Concurrency 22549
31 rayon
1.4.1
2015-12-10
2020-09-29
Concurrency 22548
32 itoa
0.4.6
2016-06-25
2020-06-16
Value formatting 22462
33 iovec
0.1.4
2017-03-15
2019-10-09
API bindings
Network programming
22384
34 futures-core-preview
0.2.3
2018-06-26
2019-09-26
- 22382
35 rustc-std-workspace-alloc
1.0.0
2019-02-06
2019-02-06
- 22377
35 futures-channel-preview
0.2.2
2018-06-26
2019-09-26
- 22377
37 futures-sink-preview
0.2.2
2018-06-26
2019-09-26
- 22376
37 futures-io-preview
0.2.2
2018-06-26
2019-09-26
- 22376
39 futures-util-preview
0.2.2
2018-06-26
2019-09-26
- 22375
40 futures-executor-preview
0.2.2
2018-06-26
2019-09-26
- 22373
41 bumpalo
3.4.0
2018-11-24
2020-06-01
Memory management
No standard library
Rust patterns
22360
42 sha1
0.6.0
2014-11-21
2018-02-11
- 22359
43 wasm-bindgen-shared
0.2.68
2018-03-06
2020-09-09
- 22353
44 wasm-bindgen-backend
0.2.68
2018-04-03
2020-09-09
- 22352
44 wasi
0.10.0+wasi-snapshot-preview1
2019-03-27
2020-06-03
No standard library
WebAssembly
22352
46 base-x
0.2.6
2016-03-30
2019-10-14
Value formatting 22351
47 wasm-bindgen-macro-support
0.2.68
2018-08-13
2020-09-09
- 22350
48 wasm-bindgen-macro
0.2.68
2018-03-06
2020-09-09
- 22349
48 stdweb-internal-runtime
0.1.5
2018-08-27
2019-09-29
API bindings
GUI
Web programming
22349
48 stdweb-derive
0.5.3
2018-02-21
2019-10-10
API bindings
GUI
Web programming
22349
48 discard
1.0.4
2018-02-25
2018-09-17
Memory management 22349

開発被依存数

順位 名前
バージョン
作成日
更新日
カテゴリー 被依存数
1 rand
0.7.3
2015-02-03
2020-01-10
Algorithms
No standard library
1471
2 criterion
0.3.3
2017-12-02
2020-06-29
Development tools::Profiling 1163
3 tokio
0.2.22
2016-07-01
2020-07-22
Asynchronous
Network programming
1137
4 env_logger
0.7.1
2015-01-27
2019-10-17
Development tools::Debugging 868
5 serde_json
1.0.58
2015-08-07
2020-09-30
Encoding 843
6 bindgen
0.55.1
2014-11-21
2020-08-24
Development tools::FFI
External FFI bindings
704
7 tempfile
3.1.0
2015-04-14
2019-07-01
- 682
8 cc
1.0.60
2014-12-16
2020-09-16
Development tools::Build Utils 676
9 quickcheck
0.9.2
2014-11-21
2020-01-14
Development tools::Testing 540
10 lazy_static
1.4.0
2014-11-23
2020-01-13
Memory management
No standard library
Rust patterns
522
11 hyper-rustls
0.21.0
2016-10-08
2020-07-05
- 498
12 serde
1.0.116
2014-12-05
2020-09-11
Encoding 442
13 pkg-config
0.3.18
2014-11-11
2020-07-11
- 408
14 serde_derive
1.0.116
2016-08-29
2020-09-11
- 394
15 pretty_assertions
0.6.1
2017-03-26
2019-02-21
Development tools 391
16 tempdir
0.3.7
2015-02-22
2018-03-21
Filesystem 351
17 futures
0.3.6
2016-07-31
2020-10-05
Asynchronous 338
18 version-sync
0.9.1
2017-09-18
2020-07-07
Development tools
Rust patterns
262
19 proptest
0.10.1
2017-06-18
2020-08-15
Development tools::Testing 247
20 clap
2.33.3
2015-03-01
2020-09-19
Command-line interface 245
21 regex
1.3.9
2014-12-13
2020-05-29
Text processing 242
22 assert_cmd
1.0.1
2018-05-28
2020-03-31
Development tools::Testing 226
23 rusoto_mock
0.45.0
2017-05-20
2020-07-22
Development tools::Testing 221
24 cmake
0.1.44
2015-07-31
2020-05-18
Development tools::Build Utils 217
25 trybuild
1.0.35
2019-05-06
2020-09-15
- 202
26 hex
0.4.2
2015-10-23
2020-02-18
Encoding
No standard library
188
27 async-std
1.6.5
2019-05-30
2020-09-28
Asynchronous
Concurrency
Network programming
187
28 gcc
0.3.55
2014-11-11
2018-10-08
Development tools 180
28 anyhow
1.0.33
2019-10-05
2020-10-06
Rust patterns 180
30 wasm-bindgen-test
0.3.18
2018-07-26
2020-09-09
- 177
31 log
0.4.11
2014-12-13
2020-07-16
Development tools::Debugging 171
31 bincode
1.3.1
2014-11-20
2020-06-24
Encoding
Network programming
171
33 rustc_version
0.2.3
2015-08-24
2018-08-03
- 143
34 predicates
1.0.5
2017-06-02
2020-07-18
Data structures
Rust patterns
142
34 chrono
0.4.19
2014-11-20
2020-09-30
Date and time 142
36 structopt
0.3.19
2017-02-03
2020-10-08
Command-line interface 139
36 reqwest
0.10.8
2016-10-16
2020-08-25
Web programming::HTTP client
WebAssembly
139
38 approx
0.3.2
2015-11-15
2019-03-17
- 134
39 bencher
0.1.5
2016-08-18
2018-01-06
Development tools::Profiling
Rust patterns
132
40 glob
0.3.0
2014-11-11
2019-03-07
Filesystem 131
41 hex-literal
0.3.1
2018-01-29
2020-08-02
- 130
42 assert_matches
1.4.0
2016-02-16
2020-10-04
No standard library 128
43 quickcheck_macros
0.9.1
2014-11-21
2020-01-11
- 127
44 pretty_env_logger
0.4.0
2016-12-29
2020-01-29
Development tools::Debugging 126
45 serde_test
1.0.116
2016-06-29
2020-09-11
- 125
46 walkdir
2.3.1
2015-09-27
2020-01-11
Filesystem 120
46 libc
0.2.79
2015-01-15
2020-10-05
External FFI bindings
No standard library
Operating systems
120
48 time
0.2.22
2014-11-13
2020-09-26
Date and time 111
49 image
0.23.10
2014-11-20
2020-09-26
Multimedia::Encoding
Multimedia::Images
110
50 rayon
1.4.1
2015-12-10
2020-09-29
Concurrency 107
50 mockito
0.27.0
2016-01-10
2020-07-25
Development tools::Testing
Web programming
107
50 doc-comment
0.3.3
2018-03-29
2020-07-01
- 107
7
2
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
7
2