Email me

译自 2026 年 10 月 24 日的英文原文。数字和来源与原文一致。 阅读英文原文 →

开发 · · 阅读约 11 分钟 · Matt Busel

归档于 Rust 指南

crates.io 下载量:我们 80% 的下载都是跟着新版本来的

crates.io 下载量:我们 80% 的下载都是跟着新版本来的

太长不看:2026 年 10 月 17 日,我把 crates.io 上我那 69 个 Rust crate 的每日下载数据全部拉了下来。89 天里一共记录了 3,676 次下载,其中 80.1% 落在新版本发布的当天或次日。一个新版本不管里面写了什么,头 24 小时大约能拿到 13 次下载,然后就归零。依赖这 69 个 crate 中任何一个的外部 crate:零。真正能说明有没有人在用你的 crate 的数字,是安静日基线,我们的是 339 次下载,大约每天 3.8 次。

先给搜索过来的朋友一个简短答案。crates.io 下载量,就是这个 crate 的 .crate 文件从 crates.io CDN 被拉取的次数,按 CDN 的请求日志统计(crates.io 在 2024 年 3 月改为基于日志计数)。自 2025 年 10 月 29 日起,只统计看起来来自 cargo 的请求(或者完全不带 user agent 的请求)。它不是用户数、安装数或项目数。某位开发者的 CI 每天在干净机器上跑 30 次 cargo build,每天就贡献 30 次下载;而某个人装了一次你的 crate 并留在本地缓存里,这辈子只贡献 1 次。

那么一个小 crate 的下载数里,到底装的是什么?下面是对一组真实 crate 的最新测量、拉取数据的脚本(直接复制,拿去跑你自己的 crate),以及怎样读懂自己的图表,既不自欺欺人地高估,也不妄自菲薄。

69 个 Rust crate 从 2026 年 7 月 20 日到 10 月 16 日每日下载量的动态柱状图,逐日填充,计数器一路涨到 3,676。夏天的日子是 1 到 3 次下载的绿色矮柱;在 9 月 25 日、26 日、30 日和 10 月 4 日这些发版日出现高高的黄绿色尖峰,10 月里随后出现淡紫色的扫库日。
全部 69 个 crate 的每一天下载量,按日子类型着色。数据来自 crates.io API,2026 年 10 月 17 日美东时间上午 7:10 拉取。柱高按平方根缩放,让安静日也看得见。

这 69 个 crate 是怎么来的

先交代一下背景,因为数据的形状直接取决于这些 crate 的发布方式。我维护着一堆 Rust 库和工具:fin-primitives(交易系统的基础组件)、tokio-prompt-orchestrator(一个带去重和熔断器的 LLM 代理)、proof-engine(一个 OpenGL 游戏引擎),还有 CoreScout,一个拆成 33 个 crate 的 workspace 的 MCP server。

它们分三波发布:2026 年 3 月 51 个版本,9 月 116 个,10 月 68 个,总共 235 个版本。10 月那波是 10 月 4 日的一次大规模发版:先让 AI 编程 agent 把每个 crate 都升级了一遍,最后我一口气全部发布。这一天在后面会很关键。

10 月 17 日美东时间上午 7:10 拉取时的累计总量:4,815 次下载。crates.io 的“recent downloads”(最近 90 天)数字是 3,677。我给这些 crate 定的目标是 100,000,所以实打实的问题是:这 4,815 里有多少是真人。

40 行代码,自己把数据拉下来

crates.io 有一个公开的 JSON API。它没有正式的版本号,但网站自己就在调用它,数据访问政策只要求两件事:每秒最多一个请求,以及一个说明你是谁的 User-Agent。三个接口就能拿到全部数据:

  • /api/v1/crates?user_id=N:某个用户拥有的全部 crate,附带累计和 90 天总量。
  • /api/v1/crates/<name>/downloads:最近 90 天的每日下载量,按版本拆分(更早的版本合并在 extra_downloads 里)。
  • /api/v1/crates/<name>/reverse_dependencies:哪些已发布的 crate 依赖了这个 crate。

这是我今天早上跑的脚本(pull.py,只删掉了挑选 JSON 字段的部分)。遇到 429 它会直接停下,不会重试,因为一个有礼貌的爬虫不会跟限流较劲:

UA = "tensorust-deepdive-research (contact: hello@tensorust.com)"
last = [0.0]

def get(path):
    wait = 1.05 - (time.time() - last[0])   # stay under 1 request/second
    if wait > 0:
        time.sleep(wait)
    last[0] = time.time()
    req = urllib.request.Request("https://crates.io" + path, headers={"User-Agent": UA})
    try:
        with urllib.request.urlopen(req, timeout=30) as r:
            return json.load(r)
    except urllib.error.HTTPError as e:
        if e.code == 429:
            print("429 - stopping"); sys.exit(2)
        raise

user = get("/api/v1/users/mattbusel")["user"]
crates = get(f"/api/v1/crates?user_id={user['id']}&per_page=100")["crates"]
for c in crates:
    n = c["name"]
    dl = get(f"/api/v1/crates/{n}/downloads")
    vers = get(f"/api/v1/crates/{n}/versions?per_page=100")
    rev = get(f"/api/v1/crates/{n}/reverse_dependencies?per_page=100")
    # ... store dl["version_downloads"], dl["meta"]["extra_downloads"], versions, rev

69 个 crate 乘以三次调用,每秒一次,大约花了三分半钟。之后还要再跑一轮 69 次调用,把版本 id 映射到版本号和发布时间,因为 downloads 接口给每一行标的是数字形式的版本 id,而不是“2.15.0”。

数据拉取的终端截图:69 个 crate;fin-primitives 561 次下载,13 个版本,1 个反向依赖;corescout-core 168,25 个反向依赖;corescout-cli 10;corescout-mcp 11;tokio-prompt-orchestrator 159;every-other-token 162。然后是分析结果:逐日曲线 13.45、1.21、0.02,第 0 天和第 1 天合计中位数 12、最小值 8、占比 0.869,rev_external 是一个空列表。
pull.py 和分析脚本的真实输出,2026 年 10 月 17 日美东时间上午 7:10 到 7:16。

如果你的 crate 超过几百个(或者想要整个 registry 的数据),别去猛刷 API:crates.io 每 24 小时会发布一份数据库转储(static.crates.io/db-dump.tar.gz),里面包含最近 90 天按天、按版本统计的下载表。

crates.io 页面给你看的是什么

下面是 fin-primitives 页面的底部,它是我下载量最高的 crate。先看图再看数字:整个夏天都是每天 0 到 3 次的低矮波动,然后在 9 月最后一周和 10 月第一周出现一根根高耸的尖峰,每根颜色都不一样。每种颜色就是一个版本。

fin-primitives 在 crates.io 上的页面截图:最近下载 258 次,累计下载 561 次,已发布 13 个版本,还有一张 90 天下载量堆叠图:整个夏天每天 0 到 3 次,在 9 月底和 10 月初发布 2.14.0 到 2.15.0 版本时冲到约 20、50 和 27。
fin-primitives 真实的 crates.io 页面,2026 年 10 月 17 日截取。每根彩色尖峰都是一个新版本。

这张图很诚实,但它只针对单个 crate。69 个 crate 就是 69 张图,不可能一张张用肉眼看,所以我把 90 天窗口里每个“crate × 日期”都归进了三类。

三种下载日

规则很简单,完全机械化:

  1. 发版窗口:这个 crate 在当天(UTC)或前一天发布了版本。
  2. 扫库日:没有发布,但当时已存在的 crate 中至少有一半在同一天被下载。
  3. 安静日:其余所有情况。

分类器的核心代码,摘自 analyze.py:

for n, c in C.items():
    for v in c['downloads']['version_downloads'] + c['downloads']['meta']['extra_downloads']:
        d = dt.date.fromisoformat(v['date'])
        if any(0 <= (d - p).days <= 1 for p in pub[n]):
            cat[v['date']][0] += v['downloads']      # publish window
        elif v['date'] in sweeps:
            cat[v['date']][1] += v['downloads']      # sweep day
        else:
            cat[v['date']][2] += v['downloads']      # quiet

2026 年 7 月 20 日到 10 月 16 日(89 个完整的 UTC 日,3,676 次下载)的结果如下:

堆叠柱状图和表格:90 天 3,676 次下载。发版当天或次日:2,945 次,占 80.1%。扫库日,即没有发布却大部分 crate 同时被下载:392 次,占 10.7%。其余的安静日:339 次,占 9.2%。
全部 69 个 crate,2026 年 7 月 20 日到 10 月 16 日(UTC 日),用本文中的脚本分类。

2,945 次下载(80.1%)落在发版窗口里。392 次(10.7%)出现在六个扫库日。339 次(9.2%)是安静日下载。单日最高是 10 月 4 日那次大发版:发布了 68 个版本,下载 1,195 次。

换句话说:如果我从没发过 9 月和 10 月那些版本,90 天的数字大概在 730 左右,而不是 3,676。

一个新版本大约拿 13 次下载,然后就没了

为了看清单独一次发布值多少下载,我选出窗口内发布、且之后至少有一周数据的所有版本(181 个),按发布后的天数把下载量对齐。

柱状图,每个新版本按发布后天数的平均下载量:第 0 天 13.45,第 1 天 1.21,第 2 天 0.02,第 3 天 0.13,第 4 天 0.57,第 5 天 1.15,第 6 天 0.27,第 7 天 0.07。
2026 年 7 月 20 日到 10 月 9 日发布的 181 个版本,每个至少有一周数据。首周下载中有 86.9% 落在第 0 天或第 1 天。

第 0 天平均下载:13.45(中位数 10)。第 1 天:1.21。第 2 天:0.02。一个版本首周下载的 86.9% 出现在第 0 天或第 1 天。所有版本中,第 0 天加第 1 天的合计最少是 8,最多是 66,中位数 12。

露馅的是下限。在有史以来发布的全部 235 个版本里,单个版本的最低累计下载是 9,中位数是 18。我有 19 个 crate 只发过一个版本,其中包括 corescout-cli 和 corescout-lineage 这类内部管道 crate,项目之外的人根本没有理由知道它们。它们的累计下载在 10 到 28 之间,中位数 15。不管有没有真人读过 README,总有某种东西会在发布后几小时内把每个新版本拉一遍。

这个“东西”是什么?数据回答不了。我的猜测(只是猜测):跟踪 crates.io 索引、新版本一出现就拉取的 registry 镜像和构建农场,再加上安全扫描和许可证扫描工具。2025 年的 user-agent 过滤就是为了剔除镜像而写的,所以能通过过滤的,要么是 cargo 本身,要么是不发 user agent 的客户端。docs.rs 也会自动为每个新版本构建文档;我没能从公开文档里确认它的拉取会不会被计数,所以我不下这个结论。

结论非常实际:在 crates.io 上,你发布的版本数量,比代码本身的任何特质都更能预测下载量。在我的 69 个 crate 里,版本数和累计下载量的相关系数是 0.82。这就是为什么 fin-primitives(13 个版本)遥遥领先,而我那些真正更有意思的 crate 反而落在后面。

扫库日:所有 crate 同时被拉一遍

第二类更让我意外。10 月 9 日什么都没发布,69 个 crate 却在同一天全部被下载,一共 184 次。10 月 13 日:69 个里有 56 个,大多各一次。这不可能是真人。一个人会装一个工具和它的依赖树,而不会在同一个下午同时装一个统计 crate、一个交易 crate、一个游戏引擎和一个 TFT 助手。

六个没有发布、但大部分 crate 同时被下载的日子:8 月 3 日,17 个 crate 中 17 个,17 次下载;9 月 29 日,50 个中 41 个,59 次;10 月 5 日,69 个中 58 个,98 次;10 月 9 日,69 个中 69 个,184 次;10 月 13 日,69 个中 56 个,61 次;10 月 15 日,69 个中 37 个,69 次。
crates.io 数据中的扫库日,每个方格代表当天已存在的一个 crate。这些日子都没有任何发布。

窗口内有六天符合这个模式:8 月 3 日(当时已存在的 17 个 crate 全部被下载)、9 月 29 日、10 月 5 日、9 日、13 日和 15 日。后面几次偏向 CoreScout 家族:10 月 6 日和 15 日下载最多的是 corescout-core、corescout-mirror 和 corescout-memory,这正好是有人解析 CoreScout 某个二进制的依赖树时会看到的情形。这就引出了一个没人提的放大因素。

Workspace 会把下载量成倍放大

CoreScout 是一个 Cargo workspace,而把 workspace 发布到 crates.io 时,每个成员都会变成一个独立的 crate。这是真实的根 Cargo.toml(节选):

corescout-core = { version = "1.2.0", path = "crates/core" }
corescout-mirror = { version = "1.2.0", path = "crates/mirror" }
corescout-substrate = { version = "1.2.0", path = "crates/substrate" }
corescout-memory = { version = "1.2.0", path = "crates/memory" }
corescout-represent = { version = "1.2.0", path = "crates/represent" }
corescout-science = { version = "1.2.0", path = "crates/science" }

corescout-core 有 25 个依赖方,全是它自己的兄弟 crate。根据反向依赖数据,corescout-mcp 的依赖树里有我另外 16 个 crate,所以在干净机器上执行一次 cargo install corescout-mcp,应该会在 17 个 crate 页面上合计显示最多 17 次下载。在全部 69 个 crate 中,反向依赖 API 列出了 178 个依赖版本,每一个都是我自己的。

这就是残酷的地方。依赖方最多的 crate 看起来“用的人”最多(有五个及以上依赖方的 11 个 crate,安静日下载中位数是 6;没有依赖方的 25 个 crate,89 天里的中位数是零),而这完全是我自己接线接出来的。

安静日基线:唯一代表真人的数字

去掉发版窗口和扫库日,剩下的是 89 天 339 次下载,69 个 crate 合计每天约 3.8 次。这是最接近“今天某处有人用我的代码构建了东西”的数字。它不是零,而且一直在涨:整组 crate 在 7 月下旬平均每天 1.7 次安静日下载,8 月 3.4 次,9 月 2.8 次,10 月上半月 8.1 次(10 月的 crate 数量多得多,所以要谨慎看待)。

大部分是一个 crate 撑起来的。光 fin-primitives 一个就占了 339 次安静日下载中的 128 次(38%),从 7 月起几乎每天一两次,远早于今年秋天那堆发版。我猜是有那么几个人或者一条 CI 流水线在定期基于它构建。这是真实的信号,而如果一句“都是发版日机器人”就把下载量打发掉,这个信号也会跟噪声一起被扔掉。

还有一个能终结一切争论的数字:外部反向依赖,0。我自己之外,没有任何已发布的 crate 用到这 69 个中的任何一个。ecosyste.ms 的 crates.io 索引是查这个的好的第二来源,因为它还会追踪那些依赖某个 crate 但自己不发布 crate 的仓库。

拿你自己的 crate 试试

  • 把你的数字拆成三个数字来读。发版窗口、扫库日、安静日。跑一下上面的分类器(或者直接看你 crate 页面上按版本区分的颜色);只有安静日那条线跟用户有关。
  • 每个版本白送 10 到 15 次下载,心里有数。如果一个全新的 crate 第一天显示 12 次下载,说明还没人发现它,这很正常。如果三周后某个普通的周二显示 12 次,那很可能有人发现了。
  • 别为了刷下载量去发版。这招确实管用(我们刚测出来它管用),也正因如此,下载量不能当作受欢迎程度的指标,任何评估你 crate 的人都会转而看依赖方和近期活跃度。
  • 数依赖方,别数下载。crates.io 的 Dependents 标签页、反向依赖 API 和 GitHub 的“Used by”才是真实采用率的体现。出现在别人依赖树里的一个 crate,抵得上一千次发版日拉取,因为对方每跑一次 CI 都会顺带下载你的,永远如此。
  • 记住 workspace 会按宽度放大数字。把一个工具拆成 30 个 crate,每次安装看起来就像 30 次下载。想要一个诚实的标题数字,就报二进制 crate 的数。
  • 定期重新拉取。API 对每个 crate 只保留 90 天的每日数据。想看趋势,就每个月把 JSON 存一份;某一天一旦滑出窗口,按天的拆分数据就永远没了。

全部数字

指标(crates.io API,2026 年 10 月 17 日美东时间上午 7:10 拉取)数值
crate 数 / 版本数69 / 235
累计下载4,815
7 月 20 日至 10 月 16 日下载(89 天)3,676
发版窗口内2,945(80.1%)
扫库日392(10.7%)
安静日339(9.2%),每天 3.8 次
新版本第 0 天下载(平均 / 中位数)13.45 / 10
首周下载中第 0 天或第 1 天的占比86.9%
单个版本的最低累计下载9
相关系数:版本数 vs 下载量0.82
外部反向依赖0

先说清楚,这不是一篇“下载量都是假的”的文章。计数规则是公开的,也很合理,安静日基线是真实的,而且在增长。这是一篇“搞清楚你看的是数字里哪一部分”的文章。我接下来在意的里程碑不是 10,000 次下载,而是第一个拥有外部依赖方的 crate。30 天后我会再拉一次数据,并公布前后对比。

如果你也在维护 crate:你的安静日基线和总量放在一起是什么样?我真心想知道,80% 落在发版窗口对小 crate 来说是常态,还是我的发版习惯让情况更糟了。

同一批项目的更多内容:tokio-prompt-orchestrator 里的语义缓存、在一台 PC 上跑 20 个编程 agent(就是升级这些 crate 的那批 agent),以及 Bevy 实例化渲染 639,000 个单位。

2026 年 10 月 17 日首发于 tensorust.com,同时发布在 dev.to。相关链接:查任意 crate:真实下载 vs 发版日机器人、crates.io 上全部 69 个 crate、运行编程 agent 的工具、tokio-prompt-orchestrator 中的语义缓存。

接着读

全部文章 →

更多中文内容

简体中文: Tensorust →

Get the receipts by email.

What my AI agents tried this week, with the real numbers: what worked, what flopped, what made money. One email when there is something worth reading.

No spam, never sold. Every email has a one-click unsubscribe.

Popular in the shop

All 124 tools →

Browse: Bundles and kitsHome, family and schoolMoney and businessAI toolsSports and fitnessMaking and hobbiesFood and drinkIndie dev and launchTrading and quant

Free: Landing page teardown iPhone app cost calculator LLM API cost calculator Paid online: $49 Launch Check App builds Articles FAQ