<rss version="2.0"><channel><title>Rust.cc</title><link>https://rust.cc</link><description>This Is Rust Crustacean Community RSS feed.</description><item><title>【Rust日报】2026-05-18 sem - 基于 Git 的语义版本控制工具</title><link>https://rustcc.cn/article?id=d15b1384-48f8-43dc-b43a-817be2dd8741</link><description><![CDATA[<h2>sem - 基于 Git 的语义版本控制工具</h2>
<p><strong>sem</strong> 是一个语义版本控制工具，构建在 Git 之上。它不显示代码行的变更，而是显示<strong>实体级别</strong>的变更（函数、方法、类等）。</p>
<h3>核心特点</h3>
<ul>
<li><strong>语义级别差异对比</strong>：显示"函数 blahh 被修改"而非"第 x-y 行变更"</li>
<li><strong>零配置</strong>：在任何 Git 仓库中直接使用，无需设置</li>
<li><strong>支持 26 种编程语言</strong>：使用 tree-sitter 解析代码</li>
<li><strong>实体重命名检测</strong>：自动识别函数/类的重命名</li>
</ul>
<h3>主要命令</h3>
<ul>
<li>
<p><strong><code>sem diff</code></strong>：实体级别的差异对比，支持重命名检测、结构化哈希和词级高亮</p>
<ul>
<li>支持多种输出格式：plain、JSON、Markdown</li>
<li>可比较工作区、暂存区、提交、任意两个文件</li>
</ul>
</li>
<li>
<p><strong><code>sem impact</code></strong>：跨文件依赖图分析，显示实体变更的影响范围</p>
<ul>
<li>可查看依赖关系、受影响的测试等</li>
</ul>
</li>
<li>
<p><strong><code>sem blame</code></strong>：实体级别的代码归属，显示谁最后修改了每个函数/类</p>
</li>
<li>
<p><strong><code>sem log</code></strong>：追踪单个实体在 Git 历史中的演变过程</p>
</li>
<li>
<p><strong><code>sem entities</code></strong>：列出文件或目录下的所有实体</p>
</li>
<li>
<p><strong><code>sem context</code></strong>：为 LLM 生成符合 token 预算的上下文（包含实体及其依赖）</p>
</li>
</ul>
<p>原文链接：https://github.com/Ataraxy-Labs/sem</p>
<h2>Rust workspace 自动发布包的困境</h2>
<h3>问题背景</h3>
<p>开发者在尝试自动发布 Rust workspace 中的多个包时遇到了两难问题：</p>
<h3>遇到的具体问题</h3>
<p><strong>方案一：<code>cargo publish --workspace</code></strong></p>
<ul>
<li>如果任何包已经发布过，命令会直接中止</li>
<li>无法跳过已发布的包继续发布其他包</li>
</ul>
<p><strong>方案二：<code>cargo workspaces publish --from-git</code></strong></p>
<ul>
<li>使用第三方工具 cargo-workspaces（https://github.com/pksunkara/cargo-workspaces）</li>
<li>存在严重缺陷：当发布失败时（非"包已存在"的原因），无法正确返回错误码</li>
<li>这在 CI/CD 流程中依赖退出码判断时会造成问题</li>
</ul>
<h3>开发者困惑</h3>
<p>作者认为这是一个非常基础的需求，对于没有现成解决方案感到惊讶，怀疑是否自己的使用方式有问题。</p>
<p>原文链接：https://www.reddit.com/r/rust/comments/1tg2hlc/is_there_a_solution_to_automatically_publish/</p>
<h2>Concord v2.0.0 发布 - Rust 编写的 Discord 终端 UI 客户端</h2>
<p>Concord 是一个使用 Rust 和 Ratatui 框架开发的 Discord 终端用户界面客户端。最新的 v2.0.0 版本带来了重要更新。</p>
<h3>主要新功能</h3>
<ul>
<li><strong>语音支持</strong>：现在可以加入语音频道并播放接收到的语音音频</li>
</ul>
<h3>其他核心功能</h3>
<ul>
<li><strong>导航功能</strong>：支持服务器(Guild)、频道、帖子、论坛和私信(DM)的导航</li>
<li><strong>消息操作</strong>：可以发送、编辑、删除、回复、置顶消息以及添加表情反应</li>
<li><strong>媒体预览</strong>：在支持的终端中提供内联图片预览</li>
<li><strong>桌面通知</strong>：支持桌面通知功能</li>
<li><strong>快捷操作</strong>：提供 Vim 风格的键盘导航</li>
</ul>
<h3>项目信息</h3>
<ul>
<li><strong>GitHub 仓库</strong>：https://github.com/chojs23/concord</li>
<li>更多功能详情可查看项目的 README 文档</li>
</ul>
<p>原文链接：https://github.com/chojs23/concord</p>
<h2>Gecko - GameCube/Wii 模拟器</h2>
<p>Gecko 是一个用 Rust 编写的跨平台 GameCube/Wii 模拟器和调试器。</p>
<h3>项目状态</h3>
<ul>
<li>仍在开发中</li>
<li>许多游戏可以运行，但大多数存在不同程度的视觉故障或完全无法运行</li>
<li>提供 GameCube 和 Wii 游戏的截图数据库以评估兼容性</li>
<li>注意：目前仅追踪 NTSC 游戏</li>
</ul>
<h3>主要特性</h3>
<ul>
<li><strong>开发目标</strong>：专注于自制软件开发和逆向工程，同时提供流畅的游戏体验</li>
<li><strong>核心技术</strong>：
<ul>
<li>PowerPC JIT（使用 Cranelift）</li>
<li>DSP JIT 和 GX 顶点解码 JIT</li>
<li>基于 wgpu 的渲染后端，支持所有主流平台</li>
<li>专用着色器编译器、JIT 和着色器缓存</li>
</ul>
</li>
<li><strong>音频系统</strong>：模块化音频后端，支持混音和导出 .wav 文件</li>
<li><strong>调试功能</strong>：
<ul>
<li>基于 egui 的高级调试界面</li>
</ul>
</li>
</ul>
<p>原文链接：https://github.com/ioncodes/gecko</p>
]]></description><pubDate>2026-05-18 01:07:35</pubDate></item><item><title>【Rust日报】2026-05-17 hi_sparse_bitset v0.9.0 发布：不可变位集与真正的零拷贝</title><link>https://rustcc.cn/article?id=a6014efc-4578-4d8f-9419-620c5e731729</link><description><![CDATA[<h2>hi_sparse_bitset v0.9.0 发布：不可变位集与真正的零拷贝</h2>
<p><code>hi_sparse_bitset</code> 这次更新最值得看的点，是把“稀疏位集”继续往更适合预计算和大数据场景的方向推了一步。v0.9.0 新增了 <code>ImmutableBitset</code>，同时让 <code>DirectBitset</code> 真正支持零拷贝读取，对需要序列化、落盘和内存映射的大规模位集场景会很有吸引力。</p>
<h3>这次版本的几个重点</h3>
<ul>
<li>新增 <code>ImmutableBitset</code>，采用线性数据结构，适合中间结果收集和预计算数据存储</li>
<li><code>DirectBitset</code> 配合新的序列化格式后，可以直接走真正的 zero-copy</li>
<li>所有位集容器都加入了内部“大小提示”，减少物化和原地 union 时的分配开销</li>
<li>增加对 <code>wide::u64x8</code> 的优化</li>
<li>配置层现在可以选择性配置数据块类型</li>
</ul>
<h3>需要注意的地方</h3>
<p>这次版本改了序列化格式，旧数据如果依赖之前的编码方式，升级时要顺手检查兼容性。但换来的收益也很直接：更低的拷贝成本、更适合 mmap 的使用方式，以及更清晰的不可变数据路径。</p>
<p>原文链接：https://github.com/tower120/hi_sparse_bitset/releases/tag/v0.9.0</p>
<h2>DualCacheFF v0.2.0 发布</h2>
<p><code>DualCacheFF</code> 是一个面向极端读写比例和抗扫描场景的并发缓存库，主打 wait-free 读路径、极低延迟和更克制的内存占用。v0.2.0 这一版把性能和工程可用性都继续往前推了一截。</p>
<h3>版本亮点</h3>
<ul>
<li>核心设计基于 QSBR / RCU 思路，强调 CPU 局部性和高吞吐</li>
<li>吞吐基准里，Zipf 工作负载下可达约 6078 万 ops/s</li>
<li>P99.9 延迟约 708ns，内存开销约 54.62 字节 / 项</li>
<li>移除了 <code>crossbeam</code> 依赖，改用自定义 wait-free 原语</li>
<li>核心引擎新增 <code>no_std</code> 支持，在需要 <code>alloc</code> 的环境里也能使用</li>
</ul>
<h3>为什么值得看</h3>
<p>Rust 缓存库不少，但把“极端场景下的吞吐、尾延迟和内存效率”同时摆到台面上对比，并且明确说明自己牺牲了什么、换来了什么，这类项目通常更有参考价值。对做高并发基础设施的人来说，这种实现思路本身就值得研究。</p>
<p>原文链接：https://crates.io/crates/dualcache-ff</p>
<h2>Raygeo：MIT 许可的 Rust/PyO3 几何库发布</h2>
<p>Raygeo 最初是为 Rayforge 激光切割应用开发的几何库，现在已经迁移到 Rust，并继续通过 PyO3 对 Python 暴露能力。它覆盖的不是一个单点小功能，而是一整套几何处理能力：路径构建、布尔运算、曲线拟合、裁剪、轮廓分析，甚至还保留了原先项目积累下来的大量测试资产。</p>
<h3>这个项目有几个看点</h3>
<ul>
<li>用 Rust 重写核心后，继续通过 PyO3 提供 Python 接口</li>
<li>支持 2D / 3D 几何路径、曲线、轮廓和多边形操作</li>
<li>集成裁剪、平滑、圆弧拟合等更偏工程侧的能力</li>
<li>设计上尽量避免把数据封装成高层 Python 对象，更强调底层性能</li>
<li>采用 MIT 许可，已有文档、GitHub 和 PyPI 发布路径</li>
</ul>
<h3>为什么它容易传播</h3>
<p>Rust 做几何计算和图形前处理并不新鲜，但这种“Rust 做核心 + Python 做落地接口”的组合，一直都很有现实价值。尤其是当它背后已经有真实应用和大量测试时，可信度会比纯 demo 型项目高不少。</p>
<p>原文链接：https://github.com/barebaric/raygeo/blob/main/docs/raygeo.md</p>
<h2>ee-editor：快速的终端文本编辑器</h2>
<p>ee-editor 是一个用 Rust 写的终端优先文本编辑器，重点不是“又一个 TUI 编辑器”，而是把大文件能力、语言感知和插件式架构一起打包成一个更完整的工程形态。</p>
<h3>它现在公开出来的几个重点</h3>
<ul>
<li>面向大文件场景设计，支持持久化 rope 存储和流式工作流</li>
<li>终端界面基于 <code>ratatui</code> 和 <code>crossterm</code></li>
<li>语言能力由 tree-sitter 驱动，可做解析、高亮和语言感知处理</li>
<li>支持 LSP 与插件集成</li>
<li>编辑器核心与前端分离，通过 JSON/RPC 通信，便于扩展和复用</li>
</ul>
<h3>为什么这条值得收进日报</h3>
<p>Rust 生态里的编辑器项目不少，但真正把“可扩展核心”“终端体验”和“超大文件处理”同时放在第一优先级的并不多。对喜欢终端工具链、又关心编辑器架构的人来说，这类项目通常很容易引起兴趣。</p>
<p>原文链接：https://github.com/ffimnsr/ee</p>
]]></description><pubDate>2026-05-17 01:08:14</pubDate></item><item><title>[开源] NeZha: 一个轻量级 AI Native IDE,  同时管理多个项目下的 AI 编程任务, 支持 Git Worktree 集成 Claude Code 和 Codex  </title><link>https://rustcc.cn/article?id=ee3727bb-beec-4a4c-b570-5d56f0dd2d06</link><description><![CDATA[<p>在两三年以前，那个时候的我可能想不到，几年后，写代码这件事原来也可以并行的。</p>
<p>这两年我尝试过各种 Vibe Coding 编程工具，到现在用的最多的还是 Claude Code 这种 CLI 工具，以前 PyCharm, Goland 这些 IDE 现在很大多时候就只用来看代码了。</p>
<p>虽然现在开七八个终端可以同时 Vibe Coding 好几个项目，但是当我真的开始这么做的时候我发现我的脑子转不过来了，每个终端都在写代码，有时候还需要点 Yes, &nbsp;而且还要在不同的 IDE, 编辑器，终端中来回切换, 上了一天班代码虽然都是 AI 写的但是人却是累到了，第二个开很多 IDE 我 16G 内存的电脑也有点吃力了。</p>
<p>为了解决自己的困扰，我开发了 NeZha 这个 AI 开发工具, NeZha 的思路很简单, 那就是去掉 AI 时代过去 IDE 那些臃肿的功能(即使不臃肿我也做不出来), 做一个 Agent 优先的编程工具，把重心放在如何管理多个项目下的 VibeCoding 任务和会话管理上，同时保留终端的功能和易用性。</p>
<p><img src="https://github.com/hanshuaikang/nezha/raw/main/docs/images/index.gif" alt="image.png"></p>
<p>NeZha 集成了 Git, CodeReview 视图, 简单的代码编辑器(支持 markdown 预览, 代码高亮), 终端，会话管理，Git Worktree 这些功能。</p>
<p>中间是一个原生的虚拟终端，使用 xterm 和 pty 实现，因此使用的时候基本和使用 claude code 或者 codex cli 是一样的。</p>
<p><img src="https://github.com/hanshuaikang/nezha/raw/main/docs/images/switch-project.png" alt="image.png"></p>
<p>任务完成之后会话会被自动可视化，之后你可以选择重新启动这个任务</p>
<p><img src="https://github.com/hanshuaikang/nezha/raw/main/docs/images/task.png" alt="image.png"></p>
<p>NeZha 使用 Tauri 开发(说实话现在有一丢丢后悔了), 后端使用 Rust 实现, 支持 Windows, Linux 和 Macos 平台, 最早 NeZha 是为了解决我自己的问题而开发的，慢慢发现身边也有一些人也有这样的痛点，所以就选择把它开源了出来，NeZha 由于完全不会上报用户信息，所以我也不知道有多少人在用它，不过我偶然会收到一些反馈和issue, 应该还是有人在用的，能帮助到其他人本身也是一件有价值的事情，我自己还是很开心的</p>
<p>说实话，我感觉 NeZha 可能不是 VibeCoding 的最终形态，不过现阶段我还是会继续迭代下去~</p>
<p>开源地址: https://github.com/hanshuaikang/nezha</p>
]]></description><pubDate>2026-05-16 04:26:15</pubDate></item><item><title>全球首款面向具身场景的 AI 原生多模态数据库 v0.2.0 重磅发布！性能飙升 2-10 倍，Rust 赋能极致效率</title><link>https://rustcc.cn/article?id=a7ff772c-1ead-415c-a33f-fc1749ac2aaa</link><description><![CDATA[<p>经过一个月的紧张冲刺，我们非常激动地宣布：<strong>全球首款面向具身场景（Embodied AI）的 AI 原生嵌入式多模态数据库—— MoteDB</strong>，迎来了一次<strong>里程碑式的重要版本升级 v0.2.0</strong>！</p>
<p>MoteDB 是一款<strong>专为边缘端设备设计的嵌入式数据库</strong>，不需要单独的服务器进程，可以直接集成到你的 Rust 应用中，为家庭机器人、AR 眼镜、工业机械臂等边缘设备提供原生多模态数据支持。</p>
<p>本次更新聚焦真实机器人、物理世界交互与多模态数据（<strong>向量、全文、时空、键值</strong>等）的深度融合，<strong>整体性能较上一代提升 2～10 倍</strong>，部分关键场景实现数量级飞跃。无论您是具身智能研究员、机器人系统工程师，还是对高性能数据库感兴趣的 Rust 开发者，都欢迎体验这一全新成果。</p>
<ul>
<li><strong>GitHub 仓库</strong>：<a href="https://github.com/motedb/motedb" rel="noopener noreferrer">https://github.com/motedb/motedb</a></li>
<li><strong>crates.io 地址</strong>：<a href="https://crates.io/crates/motedb" rel="noopener noreferrer">https://crates.io/crates/motedb</a></li>
</ul>
<hr>
<h3>🚀 性能提升一览</h3>
<p>我们在 v0.2.0 中完成了多个关键优化，以下是核心指标的测试结果：</p>
<table>
<thead>
<tr>
<th>#</th>
<th>指标</th>
<th>结果</th>
<th>提升情况</th>
</tr>
</thead>
<tbody>
<tr>
<td>1</td>
<td>INSERT</td>
<td>294.8K ops/s, 3µs 延迟</td>
<td>极高吞吐</td>
</tr>
<tr>
<td>2</td>
<td>PK Point Query</td>
<td>31.1K QPS, 30µs 延迟</td>
<td>2x+</td>
</tr>
<tr>
<td>3</td>
<td>Column Index Query</td>
<td>14.9K QPS, 67µs</td>
<td>3.4x（原 225µs）</td>
</tr>
<tr>
<td>4</td>
<td>Range Query</td>
<td>396 QPS, 2.5ms</td>
<td>7.2x（原 18ms）</td>
</tr>
<tr>
<td>5</td>
<td>Full Table Scan</td>
<td>4.3M rows/s</td>
<td>线性加速</td>
</tr>
<tr>
<td>6</td>
<td>Mixed CRUD</td>
<td>20.8K ops/s</td>
<td>稳定高效</td>
</tr>
<tr>
<td>7</td>
<td>Vector Search</td>
<td>2.4K QPS, 411µs 延迟</td>
<td>3x+</td>
</tr>
<tr>
<td>8</td>
<td>Full-Text Search</td>
<td>45.9K QPS, 21µs</td>
<td>4.5x（原 94µs）</td>
</tr>
<tr>
<td>9</td>
<td>Spatial Query</td>
<td>997 QPS, 1ms</td>
<td>接近实时</td>
</tr>
<tr>
<td>10</td>
<td>WAL Recovery</td>
<td>659.8K rows/s, 15ms</td>
<td>快速故障恢复</td>
</tr>
<tr>
<td>11</td>
<td>Prepared vs Raw</td>
<td><strong>161x</strong> 加速比（5.1M vs 31.8K QPS）</td>
<td>极致优化</td>
</tr>
</tbody>
</table>
<blockquote>
<p>括号内“原 xx”为上一版本典型延迟/QPS。所有测试在标准云服务器（16C/64G，NVMe SSD）上完成。</p>
</blockquote>
<hr>
<h3>🧠 为什么选择 MoteDB？</h3>
<p>具身智能体（如人形机器人、自动驾驶车辆、智能家居中枢）需要同时处理：</p>
<ul>
<li><strong>传感器流</strong>（图像、点云、惯性数据）→ 向量嵌入检索</li>
<li><strong>环境语义</strong>（物体、人物、位置）→ 全文 + 空间查询</li>
<li><strong>实时状态</strong>（关节角度、电量、任务队列）→ 毫秒级读写</li>
<li><strong>长期记忆</strong>（历史交互、轨迹日志）→ 高效压缩与恢复</li>
</ul>
<p>MoteDB 从底层存储引擎到查询优化器，<strong>原生设计为同时理解向量、文本、时空、标量</strong>，并允许你在一次查询中自由组合：“在 10 米范围内找到所有红色物体的最近向量匹配，并按时间排序返回前 5 条”。</p>
<p>项目提供了五大索引类型，针对不同场景进行专门优化：</p>
<table>
<thead>
<tr>
<th>索引类型</th>
<th>适用场景</th>
<th>性能提升</th>
</tr>
</thead>
<tbody>
<tr>
<td>Column Index（列索引）</td>
<td>等值/范围查询</td>
<td><strong>40x</strong></td>
</tr>
<tr>
<td>Vector Index（向量索引）</td>
<td>KNN 相似度检索</td>
<td><strong>100x</strong></td>
</tr>
<tr>
<td>Full-Text Index（全文索引）</td>
<td>BM25 文本检索</td>
<td><strong>50x</strong></td>
</tr>
<tr>
<td>Spatial Index（空间索引）</td>
<td>地理空间查询</td>
<td><strong>30x</strong></td>
</tr>
<tr>
<td>Time Series Index（时序索引）</td>
<td>时间范围查询</td>
<td><strong>20x</strong></td>
</tr>
</tbody>
</table>
<hr>
<h3>⚡ Rust 带来的硬核优势</h3>
<p>整个数据库内核<strong>完全使用 Rust 编写</strong>，代码中 Rust 占比 <strong>100.0%</strong>。我们选择 Rust 的原因很简单：既要 C++ 级的极致性能，又要内存安全和无畏并发。具体收益包括：</p>
<ul>
<li><strong>零成本抽象</strong>：向量索引（采用 <strong>FreshDiskANN</strong> 算法与 <strong>Rerank</strong> 重排序策略）、全文索引（BM25 + 分词插件）、空间索引（R-Tree）与 B+Tree 共存于同一存储引擎，无 GC 停顿。</li>
<li><strong>无惧数据竞争</strong>：多线程查询、写入、后台合并同时进行，绝无悬空指针或 use-after-free。</li>
<li><strong>轻量化嵌入式</strong>：核心数据结构内存占用 <strong>&lt; 100MB</strong>，事务吞吐量可达 <strong>10,000 TPS</strong>，批插入吞吐量高达 <strong>737,112 rows/sec</strong>（10,000 条记录），向量检索延迟 <strong>&lt; 5ms</strong>（95% 召回率）。</li>
<li><strong>跨平台编译</strong>：一个 <code>--target</code> 参数即可轻松交叉编译到 ARM、RISC-V 等嵌入式架构。</li>
</ul>
<p>我们还将数据库的客户端驱动、CLI 工具也基于 Rust 构建，从而为 RustCC 社区提供一个 <strong>端到端的高性能数据库样板</strong>。</p>
<hr>
<h3>🛠️ 核心特性一览</h3>
<ul>
<li><strong>完整 SQL 支持</strong>：包含子查询、聚合函数、JOIN 和索引管理，使用熟悉的 SQL 语法完成多模态数据操作。</li>
<li><strong>MVCC 事务</strong>：完整的事务支持，包括保存点（Savepoint）和 WAL 预写日志，确保数据强一致性。</li>
<li><strong>高性能批操作</strong>：批插入比单条插入 <strong>快 10-20 倍</strong>，适合大批量数据加载场景。</li>
<li><strong>内存友好</strong>：极致轻量化，可在树莓派等低功耗设备上流畅运行，内存占用低至 <strong>35MB</strong>。</li>
</ul>
<hr>
<h3>📦 如何获取与体验？</h3>
<ul>
<li>
<p><strong>GitHub 仓库</strong>：<a href="https://github.com/motedb/motedb" rel="noopener noreferrer">https://github.com/motedb/motedb</a></p>
</li>
<li>
<p><strong>crates.io 安装</strong>：</p>
<pre><code>cargo add motedb
</code></pre>
<p>当前版本 <strong>v0.2.0</strong>。</p>
</li>
<li>
<p><strong>Rust 快速示例</strong>：</p>
<pre><code>use motedb::{Database, Value};

fn main() -&gt; Result&lt;(), Box&lt;dyn std::error::Error&gt;&gt; {
    let db = Database::open("data.mote")?;
    db.execute("CREATE TABLE users (id INT, name TEXT, email TEXT)")?;
    db.execute("INSERT INTO users VALUES (1, 'Alice', 'alice@example.com')")?;
    let results = db.query("SELECT * FROM users WHERE id = 1")?;
    Ok(())
}
</code></pre>
</li>
<li>
<p><strong>批操作示例（10-20x 性能提升）</strong>：</p>
<pre><code>let mut rows = Vec::new();
for i in 0..10000 {
    let mut row = HashMap::new();
    row.insert("id".to_string(), Value::Integer(i));
    row.insert("name".to_string(), Value::Text(format!("User{}", i)));
    rows.push(row);
}
let row_ids = db.batch_insert_map("users", rows)?;
// 吞吐量：737,112 rows/sec
</code></pre>
</li>
<li>
<p><strong>完整文档</strong>：项目 docs 目录包含了从快速入门、安装配置、各类索引使用到性能调优的完整文档体系。</p>
</li>
</ul>
<hr>
<h3>📢 致 RustCC 开发者</h3>
<p>作为一款完全由 Rust 构建的数据库系统，我们深切受益于 Rust 社区的无私共享与严谨精神。<strong>本次 v0.2.0 发布不仅是产品迭代，更是向社区展示“Rust 能做到什么”的一个范例</strong>：从无锁数据结构到异步 I/O，从过程宏生成 SQL 解析器到基于 <code>rayon</code> 的并行扫描。</p>
<p>欢迎大家：</p>
<ul>
<li><strong>Star &amp; Watch</strong>：关注 GitHub 仓库，第一时间获取更新</li>
<li><strong>试用反馈</strong>：在实际项目中尝试 MoteDB，提交 Issue 或 PR</li>
<li><strong>参与贡献</strong>：项目还有很多有趣的技术挑战等待解决（存储引擎优化、更多索引算法、分布式扩展等）</li>
</ul>
<p>立即体验，开启 AI 原生多模态数据库的新纪元！</p>
<p><strong>GitHub 地址</strong>：<a href="https://github.com/motedb/motedb" rel="noopener noreferrer">https://github.com/motedb/motedb</a><br>
<strong>crates.io</strong>：<a href="https://crates.io/crates/motedb" rel="noopener noreferrer">https://crates.io/crates/motedb</a><br>
<strong>讨论反馈</strong>：<a href="https://github.com/motedb/motedb/issues" rel="noopener noreferrer">GitHub Issues / Discussions</a></p>
<blockquote>
<p><em>让具身智能拥有原生记忆，让 Rust 定义数据库未来。</em></p>
</blockquote>
<p>（本文欢迎转发，感谢 RustCC 社区的支持！）</p>
]]></description><pubDate>2026-05-16 01:21:27</pubDate></item><item><title>【Rust日报】2026-05-16 Burn ONNX 0.21.0 发布：支持构建时 ONNX 模型导入</title><link>https://rustcc.cn/article?id=bf2d5664-b20d-4cf7-bd26-b31e97a6b72a</link><description><![CDATA[<h2>Burn ONNX 0.21.0 发布：支持构建时 ONNX 模型导入</h2>
<p>Burn ONNX 是一个在构建时把 ONNX 模型导入 Burn 深度学习框架的工具。0.21.0 是它从 <code>tracel-ai/burn</code> 单体仓库拆出后的首个独立版本，这次不只是发了个新包，而是把测试、仓库、发布节奏和模型覆盖能力都一起拉起来了。</p>
<h3>这次版本里最值得看的点</h3>
<ul>
<li>项目已经独立托管在 <code>tracel-ai/burn-onnx</code>，有自己的 CI、issue 跟踪和发布周期</li>
<li>支持 201 个标准算子中的 160 个，注册了 174 个节点类型处理器</li>
<li>集成了 1,615 个上游 ONNX 后端测试，当前有 717 个测试通过，占可比较集合的大约 80%</li>
<li>对 ONNX opset 1 到 24 的 472 个算子 / 规范版本检查全部通过</li>
<li>真实模型验证覆盖 SDXL、Qwen、YOLO、ResNet-50、CLIP 等常见模型</li>
</ul>
<h3>它的工作方式也很 Rust</h3>
<p>在 <code>build.rs</code> 里指向 <code>.onnx</code> 文件后，工具会生成 <code>model.rs</code> 和 <code>model.bpk</code>，运行时直接加载生成结果即可，不需要额外的图解释器。对想把模型部署进纯 Rust 工程的人来说，这条路线很有吸引力。</p>
<h3>迁移也很直接</h3>
<p>旧的 <code>burn-import</code> 现在可以切到 <code>burn-onnx = "0.21"</code>。如果你之前就在关注 Burn 的模型导入能力，这次独立发布基本可以看成它进入更成熟阶段的信号。</p>
<p>原文链接：https://burn.dev/blog/release-burn-onnx-0.21.0/</p>
<h2>Toasty 0.6.0 - 新功能发布</h2>
<p>Toasty 是 Tokio 生态里一款同时面向 SQL 和 NoSQL 的异步 ORM。它从 4 月第一次发到 crates.io 之后，更新节奏相当快，0.6.0 这一版继续把“易用但不玩具”的路线往前推。</p>
<h3>0.6.0 的几个重点更新</h3>
<ul>
<li>新增延迟字段（deferred fields）和 <code>.select()</code>，可以更精细地控制查询时到底加载哪些字段</li>
<li><code>Vec&lt;标量&gt;</code> 集合字段现在有了更正式的支持：PostgreSQL 用数组，其他 SQL 数据库走 JSON，DynamoDB 走原生列表</li>
<li>增加更多查询表达式、数据库原生枚举类型、乐观版本控制和 TLS 支持</li>
</ul>
<h3>这类更新为什么值得看</h3>
<p>不少 ORM 在功能越堆越多之后，会变得越来越重。Toasty 现在更像是在补那些真正影响工程体验的能力：字段裁剪、跨数据库一致行为，以及对结构化数据的更自然建模。如果它后面把文档存储能力也补齐，可玩的空间还会更大。</p>
<p>原文链接：https://tokio.rs/blog/2026-05-15-announcing-toasty-0-6-0</p>
<h2>Rustrak v0.2.2 发布：移动端 UI、Base UI 迁移</h2>
<p>Rustrak 是一个自托管错误追踪服务器，兼容 Sentry SDK，主打单个 Rust 二进制、低空闲占用和可选的 Next.js 仪表板。v0.2.2 这次更新的重点不在“加一个小功能”，而是在把产品体验和依赖底座一起往前推。</p>
<h3>这次版本更新了什么</h3>
<ul>
<li>仪表板现在完整支持移动端响应式布局</li>
<li>问题页和事件详情页增加骨架屏，减少加载时的布局跳动</li>
<li>UI 组件从 Radix UI / shadcn 迁移到 Base UI</li>
<li>修掉了过期下拉菜单状态、侧边栏粘性高度、API 文档链接等几个实际使用中的问题</li>
<li>JavaScript 侧升级到 TypeScript 6、Node.js 22+，Rust 侧更新到 actix-web 4.13、tokio 1.52、sqlx 0.8.6、sentry 0.48.2</li>
</ul>
<h3>为什么它值得进日报</h3>
<p>Sentry 替代品并不少，但“自托管、兼容现有 SDK、单二进制、资源占用克制”这个组合始终有吸引力。Rustrak 这种项目每往前走一步，都会让 Rust 在内部平台和运维工具链里的存在感更强一些。</p>
<p>原文链接：https://github.com/AbianS/rustrak/releases/tag/v0.2.2</p>
<h2>Stella Nova - 纯 Rust 开发的 N 体引力模拟器</h2>
<p>作者 Dave 分享了自己的项目 Stella Nova：一个完全用 Rust 和 <code>wgpu</code> 构建的太空殖民地管理模拟器。它最抓人的地方，是把“游戏演示”之外的那部分工程难题也一起摆到了台前。</p>
<h3>目前公开出来的几个亮点</h3>
<ul>
<li>所有天体都遵循真实的 N 体引力物理系统</li>
<li>自研的 WarpCore 引擎可以稳定处理数千个实体</li>
<li>支持霍曼转移轨道规划、模块化空间站建造、公民 AI 和时间膨胀控制</li>
<li>安装包体积控制在 500MB 以下</li>
</ul>
<h3>为什么这条会让人多看一眼</h3>
<p>Rust 游戏项目并不罕见，但把渲染、模拟、实体规模和系统设计一起拧成一个能展示出来的个人项目，传播性会强很多。对社区来说，这种作品既能证明语言生态的上限，也更容易吸引圈外开发者点进去看看到底做到了什么程度。</p>
<p>原文链接：https://www.reddit.com/r/rust/comments/1tdz2qn/built_an_nbody_gravity_simulator_entirely_in_rust/</p>
]]></description><pubDate>2026-05-16 01:08:58</pubDate></item><item><title>Ramag —MacOS 数据库 + Redis 客户端</title><link>https://rustcc.cn/article?id=4bf4b53a-3267-4f81-89e2-1128ba3cf5e5</link><description><![CDATA[<p>不知道大家是不是和我一样：</p>
<ul>
<li>DataGrip：好用但开机吃 800MB</li>
<li>TablePlus：免费版只能开两个连接</li>
<li>Another Redis Desktop：能用，但和 DataGrip 一起跑机器就开始喘</li>
<li>Sequel Pro 早就不维护了</li>
</ul>
<p>每次开发都要在好几个 Electron 之间切来切去，风扇起飞，键盘发烫</p>
<p>所以我做了一个：<strong>Ramag</strong></p>
<blockquote>
<p>设计理念就两句话：<code>minimal by design · local by default</code></p>
</blockquote>
<p>GitHub：https://github.com/Linyuqiz/ramag</p>
<hr>
<h2>它能做什么</h2>
<p>一个 macOS 原生应用，目前装了两个工具：</p>
<h3>数据库客户端（MySQL / PostgreSQL）</h3>
<ul>
<li>连接管理 + 表结构树</li>
<li>SQL 编辑器：<strong>关键字 + 表名 + 列名补全</strong>，语法高亮</li>
<li>多语句执行，可中断（点查询出去就能 Cancel）</li>
<li>结果集<strong>行内编辑</strong>：双击单元格就改，自动生成 INSERT / UPDATE / DELETE</li>
<li>排序 / 过滤 / 导出 CSV / JSON / Markdown / SQL</li>
<li>查询历史</li>
</ul>
<h3>Redis 客户端</h3>
<ul>
<li>6 种类型全支持：String / List / Hash / Set / ZSet / Stream</li>
<li><strong>Key Trie 树</strong>：自动按 <code>:</code> 分组（比如 <code>user:1001:profile</code> 会折叠成层级）</li>
<li>TTL 可视化编辑</li>
<li>内存估算（每个 key 占多少字节一眼看到）</li>
<li>DB 切换</li>
</ul>
<hr>
<h2>为什么值得一试</h2>
<p><strong>不吃内存</strong>：原生 Rust 二进制，常驻 &lt; 100MB（DataGrip 是它 8 倍）</p>
<p><strong>开机快</strong>：1 秒内就启动了，不用看着 splash 转圈</p>
<p><strong>密码安全</strong>：连接密码存 macOS 钥匙串 + AES-GCM 加密落盘，不是明文 JSON</p>
<p><strong>离线可用</strong>：不联网、不收集、不上传，所有数据本地</p>
<hr>
<h2>技术栈</h2>
<ul>
<li>Rust nightly + GPUI（来自 Zed，原生 macOS UI）</li>
<li>sqlx（MySQL / Postgres）+ redis-rs</li>
<li>redb 本地存储 + macOS 钥匙串 + aes-gcm</li>
<li>单二进制 &lt; 20MB，DMG 安装</li>
</ul>
<p>详细架构见 <a href="https://github.com/Linyuqiz/ramag/blob/main/docs/architecture.md" rel="noopener noreferrer">docs/architecture.md</a></p>
]]></description><pubDate>2026-05-15 15:37:05</pubDate></item><item><title>【Rust日报】2026-05-15 Bun 用 Rust 重写的代码已合并</title><link>https://rustcc.cn/article?id=4ea7ef5e-02b6-4ac9-9cb5-dbd7223409c5</link><description><![CDATA[<h2>Bun 用 Rust 重写的代码已合并</h2>
<p>Bun 项目用 Rust 重写的分支已经正式合并进主代码库。这不是一个“概念验证”式的小实验，而是 Bun 主线工程演进的一次明确落地。</p>
<h3>这条消息值得关注的点</h3>
<ul>
<li>Rust 重写分支已经合并，说明相关工作进入了更接近主线交付的阶段</li>
<li>官方表示后续会发布更详细的技术博客，解释这次重写背后的设计与实现细节</li>
<li>在进入非 canary 正式版本前，仍然还有一些性能优化工作要完成</li>
<li>此外还会有一批后续 PR 继续做清理和收尾</li>
</ul>
<p>对 Rust 社区来说，这类成熟项目把关键部分逐步迁到 Rust，通常比“新项目发布”更容易形成外溢影响：既能带来工程信号，也更容易让圈外开发者重新关注 Rust 在基础设施里的位置。</p>
<p>原文链接：https://www.reddit.com/r/rust/comments/1tcrmjs/rewrite_bun_in_rust_has_been_merged/</p>
<h2>Zyx v0.153 发布：用 Rust 从零开始构建的机器学习库</h2>
<p>作者分享了他花费数年开发的 Rust 机器学习库 Zyx。这个项目的目标不小：从底层后端到高级神经网络模块，尽量用一套纯 Rust 技术栈把完整 ML 框架搭起来。</p>
<h3>目前最有看点的部分</h3>
<ul>
<li>已完整实现 CUDA、OpenCL、WGPU 后端，PTX、HIP 和 SPIR-V 也已有部分实现</li>
<li>提供 Python 绑定，而且 wheel 体积只有约 4MB</li>
<li>借鉴 tinygrad 的思路，仅用 9 类基础节点表达全部 PyTorch 操作</li>
<li>项目与依赖总代码量大约 5 万行，整体依赖相当克制</li>
</ul>
<h3>设计上的亮点</h3>
<ul>
<li>支持动态图与分支，但采用延迟执行模型</li>
<li>优化流程可选且可自由组合，包括寄存器分块、CSE、DCE、LICM 等</li>
<li>自动微分能力已经具备</li>
<li>作者希望它最终成为一个“能在各种硬件上正确运行各种模型”的精简运行时</li>
</ul>
<h3>现阶段的现实情况</h3>
<p>性能还没有彻底打磨完成。作者给出的例子里，在 RTX 2060 上跑 MNIST 时，Zyx 每步约 0.9ms，编译后的 PyTorch 约 0.7ms。接下来它还会继续补自动调优、图分割和模式匹配这几块。</p>
<p>原文链接：https://www.reddit.com/r/rust/comments/1td7242/zyx_v0153_released_ml_from_scratch_in_rust/</p>
<h2>Scena 1.0.1 发布：Rust 原生 3D 场景图渲染器</h2>
<p>Scena 是一个 Rust 原生 3D 场景图渲染器，目标很直接：把类似 Three.js 的模型查看与场景组织工作流，带进 Rust 的原生与浏览器图形开发环境。</p>
<h3>它现在已经具备的能力</h3>
<ul>
<li>支持 glTF / GLB 加载</li>
<li>提供类型化的场景图状态管理</li>
<li>采用显式的 <code>prepare()</code> / <code>render()</code> 生命周期</li>
<li>支持原生与浏览器两条渲染路径</li>
<li>可运行在 WebGPU / WebGL2 环境中</li>
<li>支持无头渲染，适合测试与 CI 场景</li>
</ul>
<h3>为什么这类项目值得看</h3>
<p>Rust 图形生态里一直不缺底层能力，但“易上手、结构清晰、面向真实场景工作流”的 3D 场景层并不算多。Scena 想补的正是这块空白：不是只展示某个底层 API，而是把资源、场景、渲染器和交互整合成一套更完整的开发体验。</p>
<p>原文链接：https://www.reddit.com/r/rust/comments/1tden04/scena_a_rustnative_3d_scenegraph_renderer/</p>
<h2>优化 image-rs 中的模糊算法，实现 5 倍性能提升</h2>
<p>作者对 Rust <code>image</code> 库里的 <code>fast_blur</code> 做了一轮很漂亮的性能优化。在处理 u8 像素图像时，多个典型参数下都实现了大约 5 倍的加速。</p>
<h3>优化结果</h3>
<ul>
<li>σ=3：从 51.1ms 降到 8.6ms，约 5.9 倍</li>
<li>σ=7：从 51.5ms 降到 9.0ms，约 5.7 倍</li>
<li>σ=50：从 52.2ms 降到 10.2ms，约 5.1 倍</li>
</ul>
<h3>关键思路</h3>
<p>作者做完 profile 之后发现，热点主要集中在浮点转换、<code>roundf</code> 和 <code>min/max</code> 这些操作上。于是核心优化方向很明确：在 u8 图像场景里，尽量用整数运算替代浮点运算。</p>
<p>这样做之后：</p>
<ul>
<li>省掉了大量浮点转换</li>
<li>避免了高频 <code>roundf</code> 调用</li>
<li>也减少了范围限制相关的额外开销</li>
</ul>
<p>这类文章的价值不只是“某个函数更快了”，而是再次提醒大家：很多性能瓶颈并不在算法名字本身，而在实现细节里的数据表示与运算方式。</p>
<p>原文链接：https://apas.tel/blog/optimizing-image-rs-blur</p>
]]></description><pubDate>2026-05-15 01:07:58</pubDate></item><item><title>batata-基于rust兼容nacos和consul的服务配置管理平台</title><link>https://rustcc.cn/article?id=c7629cdb-0d90-4a1c-9bfe-ff5a44d38ad4</link><description><![CDATA[<h1>Batata</h1>
<p><strong>Batata</strong> 是一个基于 Rust 实现的高性能动态服务发现、配置管理和服务管理平台。完全兼容 <a href="https://nacos.io/" rel="noopener noreferrer">Nacos</a> V2/V3、grpc API 和 <a href="https://www.consul.io/" rel="noopener noreferrer">Consul</a> API，可作为云原生应用的理想替代方案。</p>
<h2>特性</h2>
<h3>核心能力</h3>
<ul>
<li><strong>配置管理</strong> - 集中式动态配置，支持实时更新</li>
<li><strong>服务发现</strong> - 服务注册、发现和健康检查</li>
<li><strong>命名空间隔离</strong> - 基于命名空间的多租户资源隔离</li>
<li><strong>集群模式</strong> - 基于 Raft 共识协议的高可用方案</li>
</ul>
<h3>API 兼容性</h3>
<ul>
<li><strong>Nacos API</strong> - 完全兼容 Nacos V2 和 V3 API（V1 API 不支持）</li>
<li><strong>Consul API</strong> - 兼容 Consul Agent、Health、Catalog、KV 和 ACL API</li>
<li><strong>gRPC 支持</strong> - 高性能双向流式通信，支持 SDK 客户端</li>
</ul>
<h3>高级特性</h3>
<ul>
<li><strong>配置导入/导出</strong> - 支持 Nacos ZIP 和 Consul JSON 格式的批量配置迁移</li>
<li><strong>灰度发布</strong> - 支持配置的渐进式发布</li>
<li><strong>认证与授权</strong> - 基于 JWT 的认证和 RBAC 权限模型，支持 LDAP、OAuth2/OIDC</li>
<li><strong>限流</strong> - 可配置的 API 限流保护</li>
<li><strong>熔断器</strong> - 故障容错的弹性模式</li>
<li><strong>监控与可观测性</strong> - 兼容 Prometheus 的指标端点</li>
<li><strong>服务网格</strong> - xDS 协议支持 (EDS, CDS, LDS, RDS, ADS)</li>
<li><strong>多数据中心</strong> - 地域感知复制与跨数据中心同步</li>
<li><strong>DNS 服务</strong> - 基于 UDP 的 DNS 服务发现</li>
<li><strong>分布式锁</strong> - 基于 Raft 的分布式锁</li>
<li><strong>AI 集成</strong> - MCP (模型内容协议) 和 A2A (Agent-to-Agent) 注册中心</li>
</ul>
<h3>性能优势</h3>
<ul>
<li><strong>快速启动</strong> - 约 1-2 秒（对比 Java 版 Nacos 的 30-60 秒）</li>
<li><strong>低内存占用</strong> - 约 50-100MB（对比 Java 版 Nacos 的 1-2GB）</li>
<li><strong>高吞吐量</strong> - 基于 Tokio 运行时的异步 I/O 优化</li>
<li><strong>高效存储</strong> - 使用 RocksDB 持久化存储，配合优化的缓存策略</li>
</ul>
<h2>与 Nacos 功能对比</h2>
<p>Batata 已实现 <strong>~98% 的 Nacos 功能</strong>，可作为生产环境的替代方案。</p>
]]></description><pubDate>2026-05-14 22:40:14</pubDate></item><item><title>【Rust日报】2026-05-14 Pyrefly v1.0 正式发布：快速的 Python 类型检查器和语言服务器</title><link>https://rustcc.cn/article?id=a99b28b7-c057-41d2-9486-ee5971b27371</link><description><![CDATA[<h2>Pyrefly v1.0 正式发布：快速的 Python 类型检查器和语言服务器</h2>
<p>Pyrefly 是一个用 Rust 编写的 Python 类型检查器和语言服务器，如今已经来到稳定的 v1.0，可正式用于生产环境。这个项目自 2025 年中发布 alpha、同年 11 月进入 beta 之后，已经连续迭代了 60 多个小版本。</p>
<h3>这次 1.0 值得关注的点</h3>
<ul>
<li>已被 Meta 的 Instagram 开发团队采用，也进入了 PyTorch、NumPy、Pandas、JAX 等大型代码库的工作流</li>
<li>相比 beta 阶段，PyTorch 上的完整类型检查速度提升约 34%</li>
<li>编辑器里的增量诊断更新最高可快 125 倍，官方给出的 PyTorch 实测延迟从 2.4 秒降到 19 毫秒</li>
<li>新增 <code>basic / legacy / strict</code> 等预设，降低首次接入门槛，也更方便从 mypy / pyright 迁移</li>
<li>继续扩展对 Pydantic、Django、IDE 场景以及 AI 编码工作流的适配</li>
</ul>
<h3>为什么它会成为今天的头条</h3>
<p>一方面，这是一个 Rust 写成、并且真正打进主流 Python 开发生态的基础工具；另一方面，Pyrefly 这次已经不只是“又一个类型检查器”，而是在性能、误报控制和 IDE 体验上，开始形成自己的产品辨识度。</p>
<p>原文链接：https://pyrefly.org/blog/v1.0/</p>
<h2>gpu-video 0.4.0 发布：新增 H.265 编码器，AV1 也在路上</h2>
<p>gpu-video（原名 vkvideo）是一个与 wgpu 集成的硬件视频编解码库，核心目标是把 Vulkan Video 的能力以更顺手的 Rust API 暴露出来。这次 0.4.0 更新，最值得看的有两件事：编码器架构重构，以及 H.265 编码支持正式落地。</p>
<h3>主要更新</h3>
<ul>
<li>编码器代码被拆成“驱动决策层 + 编解码器特定 trait”两部分，后续扩展其他编码格式会容易很多</li>
<li>新增 H.265 / HEVC 编码器，在低码率下相比 H.264 有明显画质优势</li>
<li>项目团队已经把下一步目标放到 AV1 编码支持上</li>
<li>这是项目改名为 gpu-video 之后的首个版本，也意味着它的目标不再只局限在 Vulkan 这个品牌名称之下</li>
</ul>
<h3>一个值得继续观察的方向</h3>
<p>这类“视频编解码 + wgpu + 不离开 GPU 内存”的 Rust 基础设施，正在慢慢变成图形、多媒体和实时处理场景里的关键拼图。对做播放器、转码链路、视频合成或可视化工具的人来说，gpu-video 的可用性会越来越有意思。</p>
<p>原文链接：https://www.reddit.com/r/rust/comments/1tc21kd/gpuvideo_formerly_vkvideo_040_a_new_encoder/</p>
<h2>QRISM：8 色二维码把容量拉到传统 QR 的 3 倍</h2>
<p>QRISM 是一个很抓眼球的实验项目：它不满足于传统黑白二维码，而是把黑、白、蓝、黄、红、绿、品红、青 8 种颜色一起拉进编码系统里。因为每种颜色可以表示 3 比特信息，理论上就能把单个二维码的容量提升到传统 QR 的 3 倍。</p>
<h3>这个想法为什么有意思</h3>
<ul>
<li>它没有完全推翻 QR 体系，而是尽量沿用现有 ISO QR 规范里的码字结构、纠错块、掩码和对齐图案</li>
<li>读码器实现也不是停留在概念验证，作者强调自己在可靠性和性能上做了很多工作</li>
<li>按作者描述，当前读取器在测试中已经优于 rqrr，并接近 zxing 的基准质量</li>
</ul>
<h3>现阶段要怎么理解它</h3>
<p>这还不是一个“明天就能用手机相机扫起来”的标准替代品，更像是一个很典型的 Rust 社区项目：把底层编码、图像处理和工程实现缝在一起，做出一个足够完整、又足够有想象力的原型。</p>
<p>原文链接：https://www.reddit.com/r/rust/comments/1tb272i/qrism_a_high_capacity_qr_code/</p>
<h2>mpsc 通道的隐藏成本：Tokio 每条通道默认先吃掉 32 个槽位</h2>
<p>这篇文章不是新库发布，而是一篇非常有含金量的性能分析。作者在排查 Rust 反向代理的内存占用时发现，Tokio 的 <code>mpsc</code> 通道即使只设容量 1，实际也会先为 32 个元素预分配空间。</p>
<h3>文章里最关键的发现</h3>
<ul>
<li><code>BigStruct</code> 大小约 1024 字节时，创建通道后实际直接吃掉约 32KB，而不是直觉里的 1KB</li>
<li>初始内存成本近似等于“消息大小 × 32 + 额外固定开销”</li>
<li>这个行为来自 Tokio <code>mpsc</code> 内部按 32 个元素为一个 block 进行分配</li>
<li>对低吞吐、但通道数量很多的系统来说，这个默认策略可能带来相当可观的内存浪费</li>
</ul>
<h3>为什么值得 Rust 开发者看</h3>
<p>很多人会把通道容量理解成“我想缓冲多少条消息”，但底层实现的真实成本经常不是这么算的。这篇文章把问题拆得很清楚，也提醒了我们：性能优化里，抽象背后的实现细节往往比 API 名字更重要。</p>
<p>原文链接：https://blog.howardjohn.info/posts/mpsc-cost/</p>
]]></description><pubDate>2026-05-14 01:12:47</pubDate></item><item><title>【Rust日报】2026-05-13 IBM s390 架构获得 Linux 内核 Rust 支持</title><link>https://rustcc.cn/article?id=f9cedbc5-324e-4136-9155-d9cdb8847adc</link><description><![CDATA[<h2>IBM s390 架构获得 Linux 内核 Rust 支持</h2>
<p>IBM s390 成为最新获得 Linux 内核 Rust 支持的 CPU 架构，排在 x86_64、ARM、ARM64、LoongArch 和 RISC-V 之后，成为第六个进入这一名单的架构。</p>
<h3>关键进展</h3>
<ul>
<li>IBM 工程师 Jan Polensky 提交了首批补丁系列</li>
<li>这批改动规模不大，主要是把 s390 配置成支持 Rust 的 64 位架构</li>
<li>同时补上 Rust 所需的汇编接口，用于 WARN / BUG 报告以及静态分支</li>
<li>还调整了 bindgen 参数，以避免结构体布局冲突</li>
</ul>
<h3>当前状态</h3>
<ul>
<li>s390 目前仍需要 nightly rustc，因为还依赖 <code>-Zpacked-stack</code></li>
<li>补丁正在审查中</li>
<li>由于修改量相对克制，有机会进入接下来的 Linux v7.2 内核开发周期</li>
</ul>
<p>原文链接：https://www.phoronix.com/news/IBM-s390-Linux-Kernel-Rust</p>
<h2>ALint：代码仓库结构和规范检查工具</h2>
<p>ALint 是一个用 Rust 编写的仓库结构与规范检查工具，关注的是文件系统层面的约束，而不是传统代码语法或风格检查。它填补了 Repolinter 在 2026 年初停止维护后留下的空白。</p>
<h3>核心能力</h3>
<ul>
<li>通过单个 <code>.alint.yml</code> 文件声明仓库应有的结构、命名、内容和跨文件关系</li>
<li>支持 60 种规则类型，覆盖文件存在性、内容、命名、结构、安全、编码、Git 规范等场景</li>
<li>内置 19 个规则集，包含 rust、node、python、go、java、GitHub Actions、monorepo 等常见生态</li>
<li>支持 JSONPath 规则、跨文件关系检查，以及面向 AI 代理的结构化输出</li>
<li>提供 12 种自动修复操作，可先 <code>--dry-run</code> 预览再实际应用</li>
</ul>
<h3>性能表现</h3>
<ul>
<li>10 万文件规模的工作区大约 1.1 秒完成检查</li>
<li>100 万文件规模大约 12 秒</li>
<li>在真实仓库场景里，完整检查甚至可以做到比一次 <code>git status</code> 还快</li>
</ul>
<p>原文链接：https://alint.org</p>
<h2>AudioNimbus v0.14.0 发布：加入 Bevy 集成与多线程工具箱</h2>
<p>AudioNimbus 是 Steam Audio 的 Rust 安全包装器，这次 0.14.0 更新把重点放在 Bevy 集成和仿真管线能力上。</p>
<h3>主要更新</h3>
<ul>
<li>新增 <code>bevy</code> 模块，提供与 Bevy ECS 的一流集成</li>
<li><code>SpatialAudioPlugin</code> 会自动处理核心资源插入、仿真运行器生成，以及场景、探针、监听器和音源同步</li>
<li>新增 <code>wiring</code> 模块，用来构建符合最佳实践的多线程仿真管线</li>
<li>提供不同抽象层级，既能覆盖简单用例，也能满足更复杂的高级场景</li>
</ul>
<h3>破坏性变更</h3>
<ul>
<li>反射算法类型从原有设置改为类型化结构体</li>
<li>增加 <code>*_subset</code> 方法，只对请求的仿真类型进行阻塞</li>
<li>多个类型不再携带生命周期参数，改为拥有数据</li>
<li>回调函数现在要求 <code>Fn + Send + Sync</code></li>
</ul>
<p>原文链接：https://github.com/MaxenceMaire/audionimbus/releases/tag/0.14.0</p>
<h2>cloakrs：超快速的 PII 检测和脱敏工具</h2>
<p>cloakrs 是一个用 Rust 编写的库和命令行工具，用来检测并屏蔽文本、日志、JSON、CSV 以及数据库转储文件中的个人身份信息（PII）。</p>
<h3>主要特性</h3>
<ul>
<li>支持识别电子邮件、信用卡号、电话号码、IBAN、社会安全号等常见敏感信息</li>
<li>同时覆盖多种地区特定标识符，不只是通用字段匹配</li>
<li>可以处理文本、JSON、CSV、日志文件和 SQL 转储文件</li>
<li>不仅依赖正则，还会结合实际校验机制（如 Luhn、MOD 97、MOD 11）来降低误报率</li>
</ul>
<h3>性能与状态</h3>
<ul>
<li>在 M2 芯片上扫描 10 万行数据约需 0.5 秒</li>
<li>适合本地快速扫描和分享前脱敏场景</li>
<li>项目刚发布 v0.1.0，作者正在收集反馈</li>
</ul>
<p>原文链接：https://github.com/kadir/cloakrs</p>
]]></description><pubDate>2026-05-13 01:08:47</pubDate></item><item><title>A high-performance async Rust implementation of KCP - A Fast and Reliable ARQ Protocol built on top of Tokio.</title><link>https://rustcc.cn/article?id=29969d7b-6ba8-4f9e-908c-4004a893fee0</link><description><![CDATA[<p>https://github.com/leihuxi/rust-kcp
A high-performance async Rust implementation of KCP - A Fast and Reliable ARQ Protocol built on top of Tokio.</p>
<p>Features
Async-First Design: Built from ground up for async/await with Tokio integration
Zero-Copy: Efficient buffer management using the bytes crate
Lock-Free Buffer Pool: High-performance memory management with crossbeam
Connection-Oriented: High-level connection abstractions (KcpStream, KcpListener)
Protocol Compatible: Compatible with original C KCP implementation
Observability: Integrated tracing and metrics support
Memory Efficient: Object pooling and buffer reuse
Multiple Performance Modes: Normal, Fast, Turbo, Gaming presets
Installation
Add this to your Cargo.toml:</p>
<p>[dependencies]
kcp-tokio = "0.4"
tokio = { version = "1.0", features = ["full"] }
Quick Start
Client
use kcp_tokio::{KcpConfig, KcpStream};
use tokio::io::{AsyncReadExt, AsyncWriteExt};</p>
<p>#[tokio::main]
async fn main() -&gt; Result&lt;(), Box&gt; {
let config = KcpConfig::new().fast_mode();
let mut stream = KcpStream::connect("127.0.0.1:12345".parse()?, config).await?;</p>
<pre><code>// Send data
stream.write_all(b"Hello, KCP!").await?;

// Receive response
let mut buffer = [0u8; 1024];
let n = stream.read(&amp;mut buffer).await?;
println!("Received: {}", String::from_utf8_lossy(&amp;buffer[..n]));

Ok(())
</code></pre>
<p>}
Server
use kcp_tokio::{KcpConfig, KcpListener};
use tokio::io::{AsyncReadExt, AsyncWriteExt};</p>
<p>#[tokio::main]
async fn main() -&gt; Result&lt;(), Box&gt; {
let config = KcpConfig::realtime();
let mut listener = KcpListener::bind("127.0.0.1:12345".parse()?, config).await?;</p>
<pre><code>println!("Server listening on 127.0.0.1:12345");

while let Ok((mut stream, addr)) = listener.accept().await {
    println!("New connection from {}", addr);
    tokio::spawn(async move {
        let mut buf = [0u8; 1024];
        while let Ok(n) = stream.read(&amp;mut buf).await {
            if n == 0 { break; }
            let _ = stream.write_all(&amp;buf[..n]).await;
        }
    });
}

Ok(())
</code></pre>
<p>}
Architecture
┌─────────────────────────────────────────────────────────────┐
│                    Application Layer                         │
│              (User code using KcpStream/KcpListener)         │
├─────────────────────────────────────────────────────────────┤
│                    High-Level API Layer                      │
│                  KcpStream    KcpListener                    │
│           (AsyncRead/AsyncWrite, TCP-like interface)         │
├─────────────────────────────────────────────────────────────┤
│                    Protocol Core Layer                       │
│                       KcpEngine                              │
│        (ARQ logic, congestion control, retransmission)       │
├─────────────────────────────────────────────────────────────┤
│                    Common Layer                              │
│         KcpSegment, KcpHeader, BufferPool, Constants         │
├─────────────────────────────────────────────────────────────┤
│                    Transport Layer                           │
│          Generic Transport trait (UDP default)               │
└─────────────────────────────────────────────────────────────┘
Configuration
Performance Presets
// Gaming - ultra-low latency (3ms update interval)
let config = KcpConfig::gaming();</p>
<p>// Real-time communication (8ms update interval)
let config = KcpConfig::realtime();</p>
<p>// File transfer - high throughput
let config = KcpConfig::file_transfer();</p>
<p>// Testing with packet loss simulation
let config = KcpConfig::testing(0.1); // 10% packet loss
Performance Modes
Mode	Update Interval	Resend	Congestion Control	Use Case
Normal	40ms	0	Yes	General purpose
Fast	8ms	2	Yes	Low latency
Turbo	4ms	1	No	Maximum speed
Gaming	3ms	1	No	Real-time games
Custom Configuration
use std::time::Duration;</p>
<p>let config = KcpConfig::new()
.fast_mode()
.window_size(128, 128)
.mtu(1400)
.connect_timeout(Duration::from_secs(10))
.keep_alive(Some(Duration::from_secs(30)))
.stream_mode(true);
Examples</p>
<h1>Run performance test server</h1>
<p>cargo run --example perf_test_server -- 127.0.0.1:12345 gaming</p>
<h1>Run performance test client</h1>
<p>cargo run --example perf_test_client -- 127.0.0.1:12345</p>
<h1>Run simple echo example</h1>
<p>cargo run --example simple_echo
Testing</p>
<h1>Run all tests</h1>
<p>cargo test</p>
<h1>Run resilience tests (packet loss, reorder, concurrent connections)</h1>
<p>cargo test --test resilience_test</p>
<h1>Run benchmarks</h1>
<p>cargo bench</p>
<h1>Run with logging</h1>
<p>RUST_LOG=debug cargo test -- --nocapture</p>
<h1>Run clippy</h1>
<p>cargo clippy --all-targets -- --deny clippy::all
Documentation
Detailed documentation is available in the doc/ directory:</p>
<p>Document	Description
ARCHITECTURE.md	System architecture and design
MODULES.md	Module reference and APIs
USAGE.md	Usage guide and examples
TESTING.md	Testing guide
Performance
KCP provides significant latency improvements over TCP:</p>
<p>30-40% lower latency in typical network conditions
Better performance on lossy networks
Configurable trade-offs between latency and bandwidth
Optimizations in this Implementation
Actor-based lock-free architecture: KcpEngine runs in a single dedicated tokio task, eliminating Arc&lt;Mutex&lt;&gt;&gt; contention
Generic Transport trait: Associated Addr type with RPITIT — zero heap allocation on hot path (no Pin&lt;Box&gt;)
DashMap for packet routing: Listener uses lock-free concurrent hashmap on the hot path
Lock-free buffer pools: crossbeam::queue::ArrayQueue for zero-allocation fast path
BTreeMap receive buffer: O(log n) insertion for out-of-order packets (vs O(n) linear scan)
Zero-copy segment encoding: Flush avoids cloning segments, encodes by reference
Cached timestamps: Single syscall per input() call instead of 3+
Pre-allocated buffers: VecDeque::with_capacity based on window sizes, avoiding grow overhead on send burst
Zero-copy packet handling with bytes crate
Grouped state structs for better cache locality
Configurable update intervals (3-40ms)
Batch ACK processing
Use Cases
Gaming: Ultra-low latency for real-time multiplayer
VoIP/Video: Real-time communication
Live Streaming: Low-latency data delivery
File Transfer: Reliable bulk data transfer
IoT: Efficient communication for constrained devices
Compatibility
Protocol: Compatible with original C KCP
Rust: Edition 2021, stable toolchain
Tokio: 1.0+
License
MIT License - see LICENSE file.</p>
<p>Contributing
Contributions are welcome! Please feel free to submit a Pull Request.</p>
<p>Resources
Original KCP Protocol
KCP Protocol Documentation
Tokio Documentation
Benchmarks
Criterion benchmarks measure engine-level throughput and latency:</p>
<p>cargo bench
Benchmark	Description
engine_throughput	10/100/500 x 1KB messages
engine_small_messages	1000 x 64B messages
engine_large_message	Single 16KB/64KB message fragmentation + reassembly
Version History
v0.4.0: Extract kcp-core as standalone protocol crate, restructure source layout (src/ → kcp/, flatten async_kcp/)
v0.3.7: Fix ACK window/UNA fields, generic Transport trait with RPITIT, resilience tests, criterion benchmarks
v0.3.4: Engine refactoring, lock-free buffer pools, documentation
v0.3.3: Performance optimizations, sub-millisecond latency
v0.3.1: Full async support, comprehensive configuration
v0.2.x: Performance improvements and bug fixes
v0.1.x: Initial implementation</p>
]]></description><pubDate>2026-05-11 07:11:35</pubDate></item><item><title>mace：又一个嵌入式 key-value 存储</title><link>https://rustcc.cn/article?id=e2ec9976-8f93-4c2e-b63e-5d4419f55631</link><description><![CDATA[<p>mace 是一个 Rust 实现的嵌入式 KV 引擎，结合了 B+ 树的读性能和 LSM 树的写吞吐，在读多写少和扫描场景下有明显的性能优势。</p>
<hr>
<h2>核心能力</h2>
<ul>
<li><strong>混合架构</strong>：兼顾 B+ 树读速与 LSM 树写吞吐</li>
<li><strong>MVCC 并发</strong>：非阻塞的并发读写</li>
<li><strong>闪存优化</strong>：面向 SSD/NVMe 的 log-structured 设计</li>
<li><strong>大值分离</strong>：独立 Blob 存储，减少写放大</li>
<li><strong>ACID 事务</strong>：完整的事务支持</li>
</ul>
<hr>
<h2>性能数据</h2>
<table>
<thead>
<tr>
<th>场景</th>
<th>吞吐量提升</th>
</tr>
</thead>
<tbody>
<tr>
<td>随机读</td>
<td>2.4x</td>
</tr>
<tr>
<td>范围扫描</td>
<td>3.5x</td>
</tr>
<tr>
<td>读 heavy 混合负载</td>
<td>2.3x</td>
</tr>
<tr>
<td>写 heavy 混合负载</td>
<td>0.76x</td>
</tr>
</tbody>
</table>
<blockquote>
<p>注：以上为与 RocksDB 对比的中位数倍数。</p>
</blockquote>
<hr>
<h2>适用场景</h2>
<ul>
<li>需要高并发读写的嵌入式服务（尤其是 mixed/read-heavy 负载）</li>
<li>写入吞吐敏感的本地存储层（中小 value 场景优势更明显）</li>
<li>混合读写 + 扫描的业务</li>
<li>需要本地事务和 MVCC 的 Rust 应用</li>
</ul>
<hr>
<h2>地址</h2>
<ul>
<li>源码：<a href="https://github.com/abbycin/mace" rel="noopener noreferrer">https://github.com/abbycin/mace</a></li>
<li>Benchmark 脚本：<a href="https://github.com/abbycin/kv_bench" rel="noopener noreferrer">https://github.com/abbycin/kv_bench（scale 分支）</a></li>
</ul>
<blockquote>
<p>mace 还在非常早期的阶段，目前还在努力提升稳定性以及对特定workload进行优化...</p>
</blockquote>
<p><strong>0.0.29 版更新</strong></p>
<table>
<thead>
<tr>
<th>Workload</th>
<th align="right">Mace胜OPS</th>
<th align="right">OPS中位数比 (Mace/RocksDB)</th>
<th align="right">Mace胜p99</th>
<th align="right">p99中位数比 (Mace/RocksDB)</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>W1</code> (95R/5U, uniform)</td>
<td align="right">16 / 16</td>
<td align="right"><strong>2.3x</strong></td>
<td align="right">5 / 16</td>
<td align="right"><strong>1.0x</strong></td>
</tr>
<tr>
<td><code>W2</code> (95R/5U, zipf)</td>
<td align="right">16 / 16</td>
<td align="right"><strong>1.5x</strong></td>
<td align="right">11 / 16</td>
<td align="right"><strong>0.5x</strong></td>
</tr>
<tr>
<td><code>W3</code> (50R/50U)</td>
<td align="right">15 / 16</td>
<td align="right"><strong>1.4x</strong></td>
<td align="right">9 / 16</td>
<td align="right"><strong>0.5x</strong></td>
</tr>
<tr>
<td><code>W4</code> (5R/95U)</td>
<td align="right">12 / 16</td>
<td align="right"><strong>1.3x</strong></td>
<td align="right">7 / 16</td>
<td align="right"><strong>1.0x</strong></td>
</tr>
<tr>
<td><code>W5</code> (70R/25U/5S)</td>
<td align="right">15 / 16</td>
<td align="right"><strong>2.1x</strong></td>
<td align="right">16 / 16</td>
<td align="right"><strong>0.2x</strong></td>
</tr>
<tr>
<td><code>W6</code> (100% scan)</td>
<td align="right">16 / 16</td>
<td align="right"><strong>4.6x</strong></td>
<td align="right">15 / 16</td>
<td align="right"><strong>0.2x</strong></td>
</tr>
</tbody>
</table>
]]></description><pubDate>2026-03-09 11:56:39</pubDate></item><item><title>🌱 Rudis 0.4.0 发布，一个高性能内存数据库</title><link>https://rustcc.cn/article?id=682d0f5e-15ec-4138-aff6-d045fb529a7e</link><description><![CDATA[<p>项目介绍：</p>
<p>Rudis 是一个采用 Rust 语言编写得高性能键值存储系统，旨在利用 Rust 语言的优势来重新复现 Rudis 的核心功能，以满足用户对高性能、可靠性和安全性的需求，同时保证与 Rudis API 的兼容。</p>
<p>跨平台，兼容 windows、linux 系统架构。 兼容 字符串、集合、哈希、列表、有序集合数据结构。 提供 rdb 与 aof 机制以支持数据备份和恢复。 拥有卓越的处理速度和即时响应能力。 兼容 Rudis 的命令和协议规范。</p>
<p>欢迎在 GitHub 上关注我们的项目发展轨迹：</p>
<p>👉 https://github.com/lunar-landing/rudis</p>
<p>更新日志：</p>
<ul>
<li>新增 List 数据结构 Blpop、Brpop 命名。</li>
<li>新增 Hash 数据结构 HSCAN 命令，支持 MATCH 和 COUNT 参数。</li>
<li>新增 String 数据结构 SETEX、PSETEX、SETNX、SETBIT、GETBIT、BITCOUNT、BITOP 命令。</li>
<li>新增 Set 数据结构 SRANDMEMBER、SDIFFSTORE、SINTERSTORE、SMOVE 命令。</li>
<li>新增 HyperLogLog 数据结构及 PFADD、PFCOUNT、PFMERGE 命令。</li>
<li>重构 SortedSet 底层实现，采用 HashMap + SkipList 架构提升性能，并支持 bincode 序列化。</li>
<li>修复 SETEX/PSETEX 过期记录清理逻辑以及系统时间倒退导致的 RDB 调度 Panic 问题。</li>
</ul>
]]></description><pubDate>2026-02-03 03:12:19</pubDate></item><item><title>我做了一个独立开发者行情板，想试着对抗一次内卷</title><link>https://rustcc.cn/article?id=7a4bcdcd-4650-425b-92a4-6ef65838534b</link><description><![CDATA[<h1>接私活这几年，我发现我们根本不知道「合理报价」是多少</h1>
<p>这几年接私活、做外包、做独立项目，有一个问题一直困扰我：</p>
<blockquote>
<p><strong>我们其实不知道一个项目「合理的价格」是多少。</strong></p>
</blockquote>
<p>不是技术难度不知道，而是——<br>
你不知道别人真实成交是多少，只能靠猜、靠平台最低价、靠「听说」。</p>
<p>需求方会说：</p>
<blockquote>
<p>「别人比你便宜一半。」</p>
</blockquote>
<p>开发者只能纠结：</p>
<blockquote>
<p>「我是报高了，还是别人报低了？」</p>
</blockquote>
<p>时间久了，就变成大家都在往下试探，<br>
<strong>内卷不是某个人的选择，而是信息不透明的结果。</strong></p>
<hr>
<h2>我已经做了什么</h2>
<p>我先从自己开始。</p>
<p>我把自己这几年做过的一些真实项目整理出来，包括：</p>
<ul>
<li>项目类型</li>
<li>实际成交价格</li>
<li>大概工期</li>
<li>是否反复改需求</li>
<li>是否包含售后</li>
</ul>
<p>做成了一个 <strong>独立开发者行情板</strong>。</p>
<p>目前一共 <strong>23 个案例</strong>：</p>
<ul>
<li>大部分是我自己的真实成交</li>
<li>少部分是朋友的</li>
<li>也有几个是匿名提交的</li>
</ul>
<p>我不回避这个事实：<br>
<strong>数据现在还很少，而且并不「漂亮」。</strong></p>
<p>但它至少是真实的。</p>
<hr>
<h2>为什么我需要更多人，而不是「更多数据」</h2>
<p>我一个人的案例，其实没什么意义。</p>
<p>但如果有：</p>
<ul>
<li>50 个</li>
<li>100 个</li>
<li>200 个</li>
</ul>
<p>来自不同背景、不同技术栈、不同城市的真实案例，<br>
至少可以做到一件事：</p>
<blockquote>
<p><strong>让后来的人，在报价时有一个不被平台最低价绑架的参考。</strong></p>
</blockquote>
<p>你不需要证明你多厉害，<br>
也不需要报一个「体面」的价格，<br>
<strong>真实比好看重要。</strong></p>
<hr>
<h2>关于匿名和安全</h2>
<p>我知道大家最担心什么，所以我一开始就做了两件事：</p>
<ul>
<li>提供 <strong>匿名提交</strong></li>
<li>不要求任何可追溯身份信息</li>
</ul>
<p>目前有两个入口：</p>
<p><a href="https://test-cigsro9bfq3z.feishu.cn/share/base/form/shrcnoJFwnYGX1E8NKW6qjpNJ6X?from=navigation" rel="noopener noreferrer">飞书表单</a>
<a href="https://market.fxlogo.site" rel="noopener noreferrer">行情板网站</a></p>
<p>不署名、不展示来源、不做商业售卖。<br>
你可以只写你愿意写的字段。</p>
<hr>
<h2>说一句更远一点的想法（不画饼）</h2>
<p>行情板不是终点。</p>
<p>我真正想做的，是一个 <strong>不竞价、不抽佣、不负责售后</strong> 的撮合平台，<br>
只做一件事：</p>
<blockquote>
<p><strong>把预算真实的需求方，和愿意按合理价格做事的开发者，匹配到一起。</strong></p>
</blockquote>
<p>行情板只是前战，是定价共识的基础。<br>
如果连「合理价格区间」都没有，<br>
任何撮合都会退化成比谁便宜。</p>
<p>我不确定这条路能走多远，<br>
但至少想先试一次。</p>
<hr>
<h2>最后</h2>
<p>如果你愿意贡献一个案例：</p>
<ul>
<li>成功的</li>
<li>失败的</li>
<li>觉得自己报低了的</li>
<li>或者被压价压得很难受的</li>
</ul>
<p>都可以。</p>
<p>如果你不想提交，也没关系，<br>
<strong>至少希望这个东西能让你下次报价时，心里多一点底气。</strong></p>
]]></description><pubDate>2026-02-02 10:25:00</pubDate></item><item><title>低成本 AI 赋能首选！算纽 GPUNexus 聚合全球算力，MaaS 服务直达业务核心</title><link>https://rustcc.cn/article?id=d599b7f7-9c0b-4fe6-8396-133b85abbe30</link><description><![CDATA[<p>算纽GPUNexus定位全球 GPU 资源智能调度枢纽，致力于构建低成本、高弹性的下一代分布式 AI 计算生态。我们的核心服务模式：</p>
<ul>
<li>
<p>算力层聚合：广泛接入全球闲散 GPU 算力资源，通过标准化调度技术实现算力的统一管理与高效利用；</p>
</li>
<li>
<p>服务层赋能：在聚合算力之上深度部署 MaaS 模型服务，客户无需投入高昂成本搭建算力与模型架构，只需通过简洁的大模型接口，即可按需调用 AI 能力，快速赋能业务创新。</p>
</li>
</ul>
<p>算纽（GPUNexus）打通算力资源与模型应用的壁垒，让 AI 服务更便捷、更普惠。</p>
<h1>2. 产品形态</h1>
<h2>2.1. 算力资产分享</h2>
<p>算纽算力资产分享产品，核心打破算力孤岛，依托智能调度技术，实现各类计算资源一键接入、整合与统一调度，激活分散算力价值。</p>
<p>产品支持全场景接入，覆盖算力中心、企业服务器等专业设备及个人电脑、手机等终端，实现“云-边-端”全域覆盖。无论闲置算力拥有方（企业/机构/个人）还是算力需求方，均可通过平台精准匹配、高效流转。</p>
<p>无需复杂配置即可快速上线，智能调度实现供需实时匹配，既提升算力利用率，又帮助需求方降本、分享方变现，构建互利共赢的算力生态。</p>
<h2>2.2. MAAS服务</h2>
<p>算纽 MaaS服务，一站式整合30 余款主流开源大模型矩阵，囊括 DeepSeek、Qwen、GLM、Kimi、MiniMax 等明星模型，深度覆盖编程开发、学术研究与论文创作、数学推理、视觉处理与多模态交互、对话与长文本处理五大核心场景。</p>
<h2>2.3. 开发者套餐</h2>
<p>算纽开发者套餐，专为学生、独立开发者及中小团队量身定制，以超高性价比解锁顶级大模型编程能力，让每一份开发需求都能高效落地。</p>
<p>套餐核心优势直击开发痛点：成本颠覆性降低，计费低至传统tokens计费的一折，大幅压缩开发成本；模型自由切换，无需冗余购买多平台会员，一键直达GLM-4.7、MiniMax-M2.1、Kimi-K2三大顶级编程模型，最新最强的模型能力随心选；高效创作不等待，生成速度媲美同类高级套餐，助力快速完成代码编写、调试、优化等核心工作。</p>
<p>更有7天免费体验限时开启！零成本即可抢先体验顶级模型的强悍编程能力，轻松开启高效开发新体验。</p>
<p>​</p>
<ul>
<li>官方网址：<a href="https://gpunexus.com/signup?aff=c1xh" rel="noopener noreferrer">https://gpunexus.com/</a></li>
<li>咨询电话：010-53650986</li>
<li>联系邮箱：data@chengfangtech.com</li>
</ul>
]]></description><pubDate>2026-01-14 02:18:35</pubDate></item><item><title>helix-kanban 终端内的多窗口看板</title><link>https://rustcc.cn/article?id=56234088-880c-4fc8-8281-726abca68b8a</link><description><![CDATA[<h1>Kanban</h1>
<p>一个终端看板应用，灵感来自 <a href="https://helix-editor.com/" rel="noopener noreferrer">Helix 编辑器</a>的键位设计。</p>
<h2>预览</h2>
<p><img src="https://raw.githubusercontent.com/menzil/helix-kanban/master/screenshoot.png" alt="Kanban TUI 截图"></p>
<h2>特性</h2>
<ul>
<li>📁 <strong>基于文件存储</strong> - 使用 Markdown 文件和 TOML 配置，易于版本控制</li>
<li>🎯 <strong>多项目支持</strong> - 支持全局项目和本地项目（<code>.kanban/</code>）</li>
<li>⌨️  <strong>Helix 风格键位</strong> - 符合直觉的键盘快捷键</li>
<li>🪟 <strong>窗口管理</strong> - 支持垂直/水平分屏，同时查看多个项目，自动保存和恢复工作区布局</li>
<li>🎨 <strong>现代 TUI</strong> - 基于 ratatui 的美观终端界面</li>
<li>📝 <strong>Markdown 支持</strong> - 任务使用 Markdown 格式，支持外部编辑器</li>
<li>🔍 <strong>任务预览</strong> - 内置预览和外部预览工具支持</li>
<li>⚙️  <strong>自动配置</strong> - 首次运行自动检测编辑器和预览器</li>
</ul>
<h2>安装</h2>
<h3>从 crates.io 安装</h3>
<pre><code>cargo install helix-kanban
</code></pre>
<h3>从源码构建</h3>
<pre><code>git clone https://github.com/menzil/helix-kanban.git
cd helix-kanban
cargo build --release
</code></pre>
<h2>快速开始</h2>
<p>首次运行会显示欢迎对话框，自动检测系统编辑器和 Markdown 预览器：</p>
<pre><code>hxk
</code></pre>
<h3>输入法切换（macOS）</h3>
<p>为了更好的输入体验，在正常模式下自动切换到英文输入法，在对话框模式（如创建/编辑任务）时保持用户的输入法。</p>
<p><strong>推荐安装 im-select 工具：</strong></p>
<pre><code># 使用 Homebrew 安装
brew install im-select

# 或者使用 curl 安装
curl -Ls https://raw.githubusercontent.com/daipeihust/im-select/master/install_mac.sh | sh
</code></pre>
<blockquote>
<p>注意：如果不安装 im-select，程序仍可正常运行，只是不会自动切换输入法。</p>
</blockquote>
<h3>配置管理</h3>
<p>查看当前配置：</p>
<pre><code>hxk config show
</code></pre>
<p>设置编辑器：</p>
<pre><code>hxk config editor nvim
hxk config editor "code --wait"
</code></pre>
<p>设置 Markdown 预览器：</p>
<pre><code>hxk config viewer glow
hxk config viewer "open -a Marked 2"
</code></pre>
<h2>键位绑定</h2>
<h3>基础导航</h3>
<table>
<thead>
<tr>
<th>键位</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>j</code> / <code>↓</code></td>
<td>下一个任务</td>
</tr>
<tr>
<td><code>k</code> / <code>↑</code></td>
<td>上一个任务</td>
</tr>
<tr>
<td><code>h</code> / <code>←</code></td>
<td>左边的列</td>
</tr>
<tr>
<td><code>l</code> / <code>→</code></td>
<td>右边的列</td>
</tr>
<tr>
<td><code>q</code></td>
<td>退出程序</td>
</tr>
<tr>
<td><code>ESC</code></td>
<td>取消/返回</td>
</tr>
<tr>
<td><code>:</code></td>
<td>命令模式</td>
</tr>
<tr>
<td><code>?</code></td>
<td>显示帮助</td>
</tr>
<tr>
<td><code>Space</code></td>
<td>打开命令菜单</td>
</tr>
</tbody>
</table>
<h3>任务操作</h3>
<table>
<thead>
<tr>
<th>键位</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>a</code></td>
<td>创建新任务</td>
</tr>
<tr>
<td><code>e</code></td>
<td>编辑任务标题</td>
</tr>
<tr>
<td><code>E</code></td>
<td>用外部编辑器编辑任务</td>
</tr>
<tr>
<td><code>v</code></td>
<td>预览任务（TUI 内）</td>
</tr>
<tr>
<td><code>V</code></td>
<td>用外部工具预览任务</td>
</tr>
<tr>
<td><code>d</code></td>
<td>删除任务</td>
</tr>
<tr>
<td><code>H</code></td>
<td>任务移到左列</td>
</tr>
<tr>
<td><code>L</code></td>
<td>任务移到右列</td>
</tr>
<tr>
<td><code>J</code></td>
<td>任务在列内下移</td>
</tr>
<tr>
<td><code>K</code></td>
<td>任务在列内上移</td>
</tr>
</tbody>
</table>
<h3>项目管理</h3>
<table>
<thead>
<tr>
<th>键位</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>n</code></td>
<td>新建本地项目 [L]</td>
</tr>
<tr>
<td><code>N</code></td>
<td>新建全局项目 [G]</td>
</tr>
<tr>
<td><code>Space f</code></td>
<td>快速切换项目</td>
</tr>
<tr>
<td><code>Space p o</code></td>
<td>打开项目</td>
</tr>
<tr>
<td><code>Space p n</code></td>
<td>创建新项目</td>
</tr>
<tr>
<td><code>Space p d</code></td>
<td>删除项目</td>
</tr>
<tr>
<td><code>Space p r</code></td>
<td>重命名项目</td>
</tr>
<tr>
<td><code>Space r</code></td>
<td>重新加载当前项目</td>
</tr>
<tr>
<td><code>Space R</code></td>
<td>重新加载所有项目</td>
</tr>
</tbody>
</table>
<h3>窗口管理</h3>
<table>
<thead>
<tr>
<th>键位</th>
<th>功能</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>Space w w</code></td>
<td>下一个窗口</td>
</tr>
<tr>
<td><code>Space w v</code></td>
<td>垂直分屏</td>
</tr>
<tr>
<td><code>Space w s</code></td>
<td>水平分屏</td>
</tr>
<tr>
<td><code>Space w q</code></td>
<td>关闭窗口</td>
</tr>
<tr>
<td><code>Space w h</code></td>
<td>聚焦左面板</td>
</tr>
<tr>
<td><code>Space w l</code></td>
<td>聚焦右面板</td>
</tr>
<tr>
<td><code>Space w j</code></td>
<td>聚焦下面板</td>
</tr>
<tr>
<td><code>Space w k</code></td>
<td>聚焦上面板</td>
</tr>
</tbody>
</table>
<h3>命令模式</h3>
<p>按 <code>:</code> 进入命令模式，支持的命令：</p>
<ul>
<li><code>:q</code> / <code>:quit</code> - 退出应用</li>
<li><code>:open</code> / <code>:po</code> - 打开项目</li>
<li><code>:new</code> / <code>:pn</code> - 创建新项目（全局）</li>
<li><code>:new-local</code> / <code>:pnl</code> - 创建新项目（本地）</li>
<li><code>:add</code> / <code>:tn</code> - 创建新任务</li>
<li><code>:edit</code> / <code>:te</code> - 编辑任务</li>
<li><code>:view</code> / <code>:tv</code> - 预览任务</li>
<li><code>:reload</code> / <code>:r</code> / <code>:refresh</code> - 重新加载当前项目</li>
<li><code>:reload-all</code> / <code>:ra</code> / <code>:refresh-all</code> - 重新加载所有项目</li>
<li><code>:vsplit</code> / <code>:sv</code> - 垂直分屏</li>
<li><code>:hsplit</code> / <code>:sh</code> - 水平分屏</li>
<li><code>:help</code> / <code>:h</code> - 显示帮助</li>
</ul>
<h2>数据存储</h2>
<h3>全局项目</h3>
<p>全局项目存储在 <code>~/.kanban/projects/</code> 目录下。</p>
<h3>本地项目</h3>
<p>在任何目录下按 <code>n</code> 创建本地项目，会在当前目录的 <code>.kanban/</code> 下存储：</p>
<pre><code>your-project/
├── .kanban/
│   └── kanban-project/
│       ├── .kanban.toml
│       ├── todo/
│       ├── doing/
│       └── done/
└── ... (你的其他文件)
</code></pre>
<h3>项目结构</h3>
<pre><code>project-name/
├── .kanban.toml          # 项目配置
├── todo/                 # Todo 任务
│   ├── 001.md
│   └── 002.md
├── doing/                # 进行中任务
│   └── 003.md
└── done/                 # 完成的任务
    └── 004.md
</code></pre>
<h3>任务文件格式</h3>
<p>任务以 Markdown 格式存储：</p>
<pre><code># 任务标题

created: 2025-12-10T10:30:00+08:00
priority: high

任务的详细描述内容...

## 子任务

- [ ] 子任务 1
- [x] 子任务 2
</code></pre>
<h3>配置文件</h3>
<p>应用配置存储在 <code>~/.kanban/config.toml</code>：</p>
<pre><code>editor = "nvim"
markdown_viewer = "glow"

# 隐藏的全局项目列表（软删除）
hidden_projects = ["old-project", "archived-project"]
</code></pre>
<h3>工作区状态保存</h3>
<p>应用会自动保存窗口布局和工作状态，下次启动时恢复：</p>
<p><strong>保存内容</strong>：</p>
<ul>
<li>分屏结构（垂直/水平分割）</li>
<li>每个窗格打开的项目</li>
<li>当前选中的列和任务</li>
<li>聚焦的窗格</li>
</ul>
<p><strong>保存位置</strong>：</p>
<ul>
<li>全局工作区：<code>~/.kanban/workspace.toml</code> - 在任何目录启动时使用</li>
<li>本地工作区：<code>.kanban/workspace.toml</code> - 在项目目录下启动时优先使用</li>
</ul>
<p><strong>使用场景</strong>：</p>
<ul>
<li>经常需要同时查看多个项目？设置好分屏布局后，下次启动自动恢复</li>
<li>在不同项目目录工作？每个目录都有自己独立的工作区布局</li>
<li>想要重置布局？使用命令 <code>:reset-layout</code> 恢复默认单窗格</li>
</ul>
<p><strong>示例工作区配置</strong> (<code>workspace.toml</code>)：</p>
<pre><code># 自动生成，通常无需手动编辑
focused_pane = 2
next_pane_id = 4

[[panes]]
id = 0
type = "horizontal_split"
left = 1
right = 2

[[panes]]
id = 1
type = "leaf"
project = "work-project"
selected_column = 1
selected_task_index = 0

[[panes]]
id = 2
type = "leaf"
project = "personal-project"
selected_column = 0
selected_task_index = 2
</code></pre>
<h2>开发</h2>
<pre><code># 运行开发版本
cargo run

# 运行测试
cargo test

# 构建 release 版本
cargo build --release
</code></pre>
<h2>致谢</h2>
<ul>
<li>键位设计灵感来自 <a href="https://helix-editor.com/" rel="noopener noreferrer">Helix Editor</a></li>
<li>UI 框架使用 <a href="https://github.com/ratatui-org/ratatui" rel="noopener noreferrer">ratatui</a></li>
</ul>
<h2>许可证</h2>
<p>MIT OR Apache-2.0</p>
]]></description><pubDate>2025-12-11 10:37:54</pubDate></item><item><title>使用 Rust 宏实现基于 Sea-ORM 的乐观锁样板代码自动化</title><link>https://rustcc.cn/article?id=1e3818da-3c6a-46eb-89ab-3e3144fc362c</link><description><![CDATA[<p>在昨天的文章中，我们讨论了乐观锁（Optimistic Locking）作为高并发场景下保证数据一致性的重要手段。但乐观锁的实现，尤其是基于版本号（Version）或时间戳（Updated At）的 <strong>CAS (Compare-and-Swap)</strong> 模式，往往需要在应用的每个 Repository 中重复编写大量的样板代码。</p>
<p>今天的核心主题是：如何利用 <strong>Rust 过程宏</strong>的强大能力，将这些繁琐的持久化逻辑自动化，让开发者只需声明字段，即可获得健壮的乐观锁支持。</p>
<hr>
<h2>宏架构：分治与协作</h2>
<p>实现一个完整的、自动化的乐观锁流程，需要宏在两个不同的代码层面进行注入和协作：</p>
<ol>
<li><strong>数据变更层</strong> (<code>ActiveModelBehavior</code>)：负责在数据写入数据库前，自动管理版本号 (<code>version</code>) 和时间戳 (<code>updated_at</code>) 的递增/更新。</li>
<li><strong>持久化操作层</strong> (<code>Repository::save</code>)：负责实现核心的原子更新逻辑，即 <strong>CAS 检查</strong>。</li>
</ol>
<h3>Part 1: ActiveModel 的预处理钩子 (<code>before_save</code>)</h3>
<p>这是我们实现乐观锁的第一步：确保在更新操作中，版本号能够正确地 <strong>自增</strong>。</p>
<p>我们通过宏注入或修改 <code>sea-orm::ActiveModelBehavior</code> Trait 的 <code>before_save</code> 钩子。</p>
<p><strong>宏注入逻辑概览：</strong></p>
<pre><code>// 宏片段：insert_active_model_behavior_impl 的核心逻辑
if need_version {
    let version_stmt = quote! {
        if insert {
            // 插入 (insert=true) 时，版本号初始化为 1
            self.version = Set(1);
        } else if self.is_changed() {
            // 更新 (insert=false) 且模型有业务字段变化时，版本号自增
            let current_version = match self.version {
                Set(v) =&gt; *v,
                _ =&gt; 0,
            };
            self.version = Set(current_version + 1);
        }
    };
}
// updated_at 逻辑类似：非插入且 is_changed 时设置为当前时间
</code></pre>
<p><strong>关键成果：</strong>
当我们在 Repository 中执行更新操作时，<code>ActiveModel</code> 已经通过 <code>before_save</code> 确保了两个重要事实：</p>
<ol>
<li>它携带着我们从数据库中读出的 <strong>旧版本号</strong>。</li>
<li>它将尝试写入的 <code>version</code> 值，是 <strong>旧版本号 + 1</strong>。</li>
</ol>
<hr>
<h3>Part 2: Repository 的原子 CAS 更新 (<code>save</code> 方法)</h3>
<p>这是乐观锁实现的核心战场，由 <code>fn create_tenant_save_impl</code> 宏片段生成。其逻辑必须严格遵循 <strong>三步走</strong> 策略，以处理成功、冲突和首次插入三种情况。</p>
<h4>Step 1: 原子 UPDATE (Compare-and-Swap)</h4>
<p>我们使用 <code>sea-orm</code> 的 <code>update_many</code> 配合 <code>filter</code> 条件，来实现原子性检查。</p>
<p>我们从聚合根 (<code>entity</code>) 中取出 <strong>旧版本</strong>（即 <code>current_version</code>），并将其作为 <code>WHERE</code> 子句的一部分。</p>
<pre><code>// 宏片段：create_tenant_save_impl 的核心 CAS 逻辑

// 从聚合获取当前版本（即期望的旧版本）
let current_version = entity_model.#optimistic_lock_field_ident();

// 1) 原子 UPDATE（带 version CAS）
let res = models::Entity::update_many()
    #id_filters // 主键和 TenantId 过滤
    // ⬇️ 核心：只有当数据库中的版本号等于旧版本号时，才允许更新 ⬇️
    .filter(models::Column::#optimistic_lock_col_ident.eq(current_version)) 
    .set(update_model.clone())
    .exec(&amp;conn)
    .await?;

if res.rows_affected &gt; 0 {
    // 成功！说明版本匹配，且更新成功写入
    // ... 事件处理并返回 Ok(())
    return Ok(());
}
</code></pre>
<p>如果 <code>rows_affected &gt; 0</code>，任务圆满完成。如果 <code>rows_affected == 0</code>，则进入下一步判断。</p>
<h4>Step 2 &amp; 3: 冲突检测与首次插入</h4>
<p>如果 CAS 更新失败（<code>rows_affected == 0</code>），我们需要区分是 <strong>版本冲突</strong>（记录存在但版本号不匹配）还是 <strong>首次插入</strong>（记录根本不存在）。</p>
<pre><code>// 2) UPDATE 未命中，检查记录是否存在
if models::Entity::find()
    #id_filters // 仅按主键和 TenantId 查找
    .one(&amp;conn)
    .await?
    .is_some()
{
    // 记录存在，但 Step 1 未命中 -&gt; 乐观锁冲突！
    return Err(#crate_root::domain::RepositoryError::optimistic_lock_error(
        "Optimistic lock conflict: Version mismatch".to_string(),
    ));
}

// 3) 记录不存在，执行首次插入
let insert_model: models::Model = entity_model.clone().try_into()?;
let mut active_model = insert_model.into_active_model();
active_model.insert(&amp;conn).await?;
// ... 事件处理并返回 Ok(())
</code></pre>
<h3>Talk is cheap, show me the code</h3>
<h4>before_save</h4>
<pre><code>fn insert_active_model_behavior_impl(input: &amp;mut ItemMod, model_config: &amp;ModelConfig) {
  let Some((_, items)) = &amp;mut input.content else {
      return;
  };

  let mut has_active_model_behavior = false;
  for item in items.iter_mut() {
      if let syn::Item::Impl(item_impl) = item
          &amp;&amp; let Some((_, path, _)) = &amp;item_impl.trait_
          &amp;&amp; path.segments.last().unwrap().ident == "ActiveModelBehavior"
      {
          has_active_model_behavior = true;
          break;
      }
  }

  if !has_active_model_behavior {
      let active_model_behavior_impl = quote! {
          #[async_trait]
          impl ActiveModelBehavior for ActiveModel {
              async fn before_save&lt;C&gt;(mut self, db: &amp;C, insert: bool) -&gt; Result&lt;Self, DbErr&gt;
              where
                  C: ConnectionTrait,
              {
                  Ok(self)
              }
          }
      };
      items.push(parse_quote!(#active_model_behavior_impl));
  }

  for item in items.iter_mut() {
      if let syn::Item::Impl(item_impl) = item
          &amp;&amp; let Some((_, path, _)) = &amp;item_impl.trait_
          &amp;&amp; path.segments.last().unwrap().ident == "ActiveModelBehavior"
      {
          let mut has_before_save = false;
          for item in item_impl.items.iter_mut() {
              if let syn::ImplItem::Fn(method) = item
                  &amp;&amp; method.sig.ident == "before_save"
              {
                  has_before_save = true;
                  break;
              }
          }

          if !has_before_save {
              let before_save_method = quote! {
                  async fn before_save&lt;C&gt;(mut self, db: &amp;C, insert: bool) -&gt; Result&lt;Self, DbErr&gt;
                  where
                      C: ConnectionTrait,
                  {
                      Ok(self)
                  }
              };
              item_impl.items.push(parse_quote!(#before_save_method));
          }

          let need_created_at = model_config
              .fields
              .iter()
              .any(|f| f.ident.as_ref().unwrap() == "created_at");
          let need_updated_at = model_config
              .fields
              .iter()
              .any(|f| f.ident.as_ref().unwrap() == "updated_at");

          let need_version = model_config
              .fields
              .iter()
              .any(|f| f.ident.as_ref().unwrap() == "version");

          if !(need_created_at || need_updated_at || need_version) {
              return;
          }

          for item in item_impl.items.iter_mut() {
              if let syn::ImplItem::Fn(method) = item
                  &amp;&amp; method.sig.ident == "before_save"
              {
                  let mut stmts = Vec::new();
                  stmts.push(quote! {
                      let now = chrono::Utc::now();
                  });

                  if need_created_at {
                      let created_at_stmt = quote! {
                          if insert {
                              self.created_at = Set(now);
                          }
                      };
                      stmts.push(created_at_stmt);
                  }
                  if need_updated_at {
                      let updated_at_stmt = quote! {
                          if insert {
                              self.updated_at = Set(now);
                          } else if self.is_changed() {
                              self.updated_at = Set(now);
                          }
                      };
                      stmts.push(updated_at_stmt);
                  }

                  if need_version {
                      let version_stmt = quote! {
                          if insert {
                              self.version = Set(1);
                          } else if self.is_changed() {
                              let current_version = match self.version {
                              Set(v) =&gt; *v,
                              _ =&gt; 0,
                          };
                              self.version = Set(current_version + 1);
                          }
                      };
                      stmts.push(version_stmt);
                  }

                  let stmts = parse_quote!({#(#stmts)*});

                  // 插入到方法体的开头
                  method.block.stmts.insert(0, stmts);
              }
          }
      }
  }
}

</code></pre>
<p>宏生成的代码示例</p>
<pre><code> impl ActiveModelBehavior for ActiveModel {
        #[allow(
            elided_named_lifetimes,
            clippy::async_yields_async,
            clippy::diverging_sub_expression,
            clippy::let_unit_value,
            clippy::needless_arbitrary_self_type,
            clippy::no_effect_underscore_binding,
            clippy::shadow_same,
            clippy::type_complexity,
            clippy::type_repetition_in_bounds,
            clippy::used_underscore_binding
        )]
        fn before_save&lt;'life0, 'async_trait, C&gt;(
            self,
            db: &amp;'life0 C,
            insert: bool,
        ) -&gt; ::core::pin::Pin&lt;
            Box&lt;
                dyn ::core::future::Future&lt;Output = Result&lt;Self, DbErr&gt;&gt;
                    + ::core::marker::Send
                    + 'async_trait,
            &gt;,
        &gt;
        where
            C: ConnectionTrait,
            C: 'async_trait,
            'life0: 'async_trait,
            Self: 'async_trait,
        {
            Box::pin(async move {
                if let ::core::option::Option::Some(__ret) =
                    ::core::option::Option::None::&lt;Result&lt;Self, DbErr&gt;&gt;
                {
                    #[allow(unreachable_code)]
                    return __ret;
                }
                let mut __self = self;
                let insert = insert;
                let __ret: Result&lt;Self, DbErr&gt; = {
                    {
                        let now = chrono::Utc::now();
                        if insert {
                            __self.created_at = Set(now);
                        }
                        if insert {
                            __self.updated_at = Set(now);
                        } else if __self.is_changed() {
                            __self.updated_at = Set(now);
                        }
                    }
                    Ok(__self)
                };
                #[allow(unreachable_code)]
                __ret
            })
        }
    }
</code></pre>
<h4>Repository::save</h4>
<pre><code>fn create_tenant_save_impl(
    crate_root: &amp;Path,
    aggregate: &amp;Path,
    args: &amp;RepositoryStructArgs,
    id_filters: &amp;TokenStream,
) -&gt; TokenStream {
    // 若指定了乐观锁字段，准备字段名/Column ident
    let optimistic_lock_field = args.optimistic_lock_field.as_ref().map(|lit| {
        let optimistic_lock_field_name = lit.value();
        let optimstic_lock_field_ident = new_id(&amp;optimistic_lock_field_name); // 用于 ActiveModel/Model 字段访问
        let optimistic_lock_col_ident = new_id(&amp;to_pascal_case(&amp;optimistic_lock_field_name)); // 用于 models::Column::Xxx
        (
            optimistic_lock_field_name,
            optimstic_lock_field_ident,
            optimistic_lock_col_ident,
        )
    });

    // 根据是否指定乐观锁字段，生成 save 的实现
    if let Some((
        optimistic_lock_field_name,
        optimistic_lock_field_ident,
        optimistic_lock_col_ident,
    )) = optimistic_lock_field
    {
        if optimistic_lock_field_name == "version" {
            quote! {
                async fn save(
                    &amp;self,
                    txn: &amp;mut TC,
                    entity: &amp;mut #crate_root::domain::EventSourcedEntity&lt;#aggregate&gt;,
                ) -&gt; Result&lt;(), #crate_root::domain::RepositoryError&gt; {
                    use #crate_root::domain::SeaOrmModelUpdater;
                    use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter};
                    use sea_orm::ActiveValue::Set;

                    let conn = txn.get_connection();
                    let entity_model: &amp;#aggregate = entity;

                    let id = entity_model.id();
                    let tenant_id = entity_model.tenant_id();

                    // 从聚合获取当前版本与期望旧版本
                    let current_version = entity_model.#optimistic_lock_field_ident();

                    // 构造用于原子更新的 ActiveModel（只写回必要列）
                    let mut update_model = models::Model::from(entity_model.clone()).into_active_model();

                    // 1) 原子 UPDATE（带 version CAS）
                    let res = models::Entity::update_many()
                        #id_filters
                        .filter(models::Column::TenantId.eq(*tenant_id))
                        .filter(models::Column::#optimistic_lock_col_ident.eq(current_version))
                        .set(update_model.clone())
                        .exec(&amp;conn)
                        .await?;

                    if res.rows_affected &gt; 0 {
                        entity.move_event_to_context(txn);
                        return Ok(());
                    }

                    // 2) UPDATE 未命中，检查记录是否存在（按主键 + tenant）
                    if models::Entity::find()
                        #id_filters
                        .filter(models::Column::TenantId.eq(*tenant_id))
                        .one(&amp;conn)
                        .await?
                        .is_some()
                    {
                        return Err(#crate_root::domain::RepositoryError::optimistic_lock_error(
                            "Optimistic lock conflict: Version mismatch".to_string(),
                        ));
                    }

                    // 3) 记录不存在，插入数据
                    let insert_model: models::Model = entity_model.clone().try_into()?;
                    let mut active_model = insert_model.into_active_model();
                    active_model.insert(&amp;conn).await?;
                    entity.move_event_to_context(txn);
                    Ok(())
                }
            }
        } else {
            // treat as timestamp update_at
            quote! {
                async fn save(
                    &amp;self,
                    txn: &amp;mut TC,
                    entity: &amp;mut #crate_root::domain::EventSourcedEntity&lt;#aggregate&gt;,
                ) -&gt; Result&lt;(), #crate_root::domain::RepositoryError&gt; {
                    use #crate_root::domain::SeaOrmModelUpdater;
                    use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter};
                    use sea_orm::ActiveValue::Set;
                    use chrono::Utc;

                    let conn = txn.get_connection();
                    let entity_model: &amp;#aggregate = entity;

                    let id = entity_model.id();
                    let tenant_id = entity_model.tenant_id();

                    // 读取实体携带的旧时间戳与准备新的时间戳
                    let current_ts = entity_model.#optimistic_lock_field_ident();

                    // 构造用于原子更新的 ActiveModel
                    let mut update_model = models::Model::from(entity_model.clone()).into_active_model();

                    // 1) 原子 UPDATE（带 updated_at CAS）
                    let res = models::Entity::update_many()
                        #id_filters
                        .filter(models::Column::TenantId.eq(*tenant_id))
                        .filter(models::Column::#optimistic_lock_col_ident.eq(current_ts))
                        .set(update_model.clone())
                        .exec(&amp;conn)
                        .await?;

                    if res.rows_affected &gt; 0 {
                        entity.move_event_to_context(txn);
                        return Ok(());
                    }

                    // 2) UPDATE 未命中，检查记录是否存在
                    if models::Entity::find()
                        #id_filters
                        .filter(models::Column::TenantId.eq(*tenant_id))
                        .one(&amp;conn)
                        .await?
                        .is_some()
                    {
                        return Err(#crate_root::domain::RepositoryError::optimistic_lock_error(
                            "Optimistic lock conflict".to_string(),
                        ));
                    }

                    // 3) 记录不存在，直接插入数据
                    let insert_model: models::Model = entity_model.clone().try_into()?;
                    let mut active_model = insert_model.into_active_model();

                    active_model.insert(&amp;conn).await?;
                    entity.move_event_to_context(txn);
                    Ok(())
                }
            }
        }
    } else {
        // no optimistic lock field -&gt; simple update/insert behavior (原始实现)
        quote! {
            async fn save(
                &amp;self,
                txn: &amp;mut TC,
                entity: &amp;mut #crate_root::domain::EventSourcedEntity&lt;#aggregate&gt;,
            ) -&gt; Result&lt;(), #crate_root::domain::RepositoryError&gt; {
                use #crate_root::domain::SeaOrmModelUpdater;
                use sea_orm::{ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter};

                let conn = txn.get_connection();

                let entity_model: &amp;#aggregate = entity;

                let id = entity_model.id();
                let tenant_id = entity_model.tenant_id();

                if let Some(mut model) = models::Entity::find()
                    #id_filters
                    .filter(models::Column::TenantId.eq(*tenant_id))
                    .one(&amp;conn)
                    .await?
                {
                    if &amp;model.tenant_id != tenant_id {
                        return Err(#crate_root::domain::RepositoryError::mapping_error(
                            format!(
                                "Tenant ID mismatch: expected {}, found {}, id: {}",
                                tenant_id, model.tenant_id, id
                            ),
                        ));
                    }

                    // 更新逻辑
                    model.update_from_aggregate_root(entity_model).await?;

                    let active_model = model.into_active_model();
                    active_model.update(&amp;conn).await?;
                } else {
                    // 创建新记录
                    let model: models::Model = entity_model.clone().try_into()?;
                    let active_model = model.into_active_model();
                    active_model.insert(&amp;conn).await?;
                }

                entity.move_event_to_context(txn);
                Ok(())
            }
        }
    }
}

</code></pre>
<p>宏生成的代码示例</p>
<pre><code>  async fn save(
        &amp;self,
        txn: &amp;mut TC,
        entity: &amp;mut core_common::domain::EventSourcedEntity&lt;TenantUser&gt;,
    ) -&gt; Result&lt;(), core_common::domain::RepositoryError&gt; {
        use core_common::domain::SeaOrmModelUpdater;
        use sea_orm::{
            ActiveModelTrait, ColumnTrait, EntityTrait, IntoActiveModel, QueryFilter,
        };
        use sea_orm::ActiveValue::Set;
        use chrono::Utc;
        let conn = txn.get_connection();
        let entity_model: &amp;TenantUser = entity;
        let id = entity_model.id();
        let current_ts = entity_model.update_at();
        let mut update_model = models::Model::from(entity_model.clone())
            .into_active_model();
        let res = models::Entity::update_many()
            .filter(models::Column::Id.eq((id.tenant_id(), id.user_id())))
            .filter(models::Column::UpdateAt.eq(current_ts))
            .set(update_model.clone())
            .exec(&amp;conn)
            .await?;
        if res.rows_affected &gt; 0 {
            entity.move_event_to_context(txn);
            return Ok(());
        }
        if models::Entity::find()
            .filter(models::Column::Id.eq((id.tenant_id(), id.user_id())))
            .one(&amp;conn)
            .await?
            .is_some()
        {
            return Err(
                core_common::domain::RepositoryError::optimistic_lock_error(
                    "Optimistic lock conflict".to_string(),
                ),
            );
        }
        let insert_model: models::Model = entity_model.clone().try_into()?;
        let mut active_model = insert_model.into_active_model();
        active_model.insert(&amp;conn).await?;
        entity.move_event_to_context(txn);
        Ok(())
    }
</code></pre>
<h3>兼容性处理</h3>
<p>宏的另一个优势是其灵活性。它能根据字段名称自动适配不同的乐观锁策略：</p>
<ul>
<li>如果检测到字段为 <code>"version"</code>，则执行版本号的 CAS 逻辑。</li>
<li>如果检测到其他时间戳字段如 <code>"updated_at"</code>，则执行基于时间戳的 CAS 逻辑。</li>
</ul>
<hr>
<h2>结论</h2>
<p>通过将 <code>before_save</code> 中的版本递增逻辑，与 <code>Repository::save</code> 中的原子 CAS 检查完美结合，我们使用 Rust 过程宏实现了一个 <strong>高内聚、低耦合</strong> 的乐观锁基础设施。</p>
<p>开发者现在可以专注于业务逻辑，而将并发控制的复杂性和样板代码完全交给宏来处理。这不仅极大地提高了开发效率，同时也确保了底层持久化操作的健壮性和一致性。</p>
]]></description><pubDate>2025-11-18 12:33:36</pubDate></item><item><title>避开数据竞态：Rust SeaORM 中的乐观锁与 Upsert 模式实践</title><link>https://rustcc.cn/article?id=7436f49b-1862-4226-90cf-b517cf0d1902</link><description><![CDATA[<p>在构建高并发的后端服务时，确保数据的最终一致性是至关重要的。特别是当业务逻辑需要执行 <strong>"更新或插入 (Upsert)"</strong> 这种复合操作时，传统的 “先查询，后更新” 模式极易陷入并发陷阱。<br>
本文将深入探讨为什么简单的操作会引发竞态条件，并介绍如何在 Rust 的 SeaORM 框架中，使用 <strong>版本号（<code>i32</code>）</strong> 实现一个健壮的 <strong>原子化乐观锁 Upsert</strong> 流程。</p>
<hr>
<h2>一、乐观锁：不是不锁，而是“巧”锁</h2>
<p>数据库的并发控制主要分为悲观锁和乐观锁。</p>
<ul>
<li><strong>悲观锁（Pessimistic Locking）：</strong> 假设冲突一定会发生。在读取数据时就对数据行进行锁定，直到事务完成。</li>
<li><strong>乐观锁（Optimistic Locking）：</strong> 假设冲突很少发生。在整个事务过程中不锁定资源，而是通过检查数据是否被修改来确认。</li>
</ul>
<p>乐观锁的核心思想是：<strong>通过一次原子性的操作来检查并修改数据，而不是依赖两次独立的数据库操作。</strong></p>
<hr>
<h2>二、没有锁的陷阱：丢失更新的竞态条件</h2>
<p>让我们以一个 <code>version: i32</code> 字段为例，来看看缺乏原子性操作会导致什么问题。</p>
<h3>场景：多人同时更新同一条记录</h3>
<ol>
<li><strong>查询（事务 A/B）：</strong> 事务 A 和事务 B 都读取了 ID=1 的记录，其 <code>version</code> 都为 <strong><code>1</code></strong>。</li>
<li><strong>更新（事务 B 提交）：</strong> 事务 B 完成修改，执行 <strong>无版本检查</strong> 的 <code>UPDATE</code> 语句，数据库中的 <code>version</code> 变为 <code>2</code>。</li>
<li><strong>更新（事务 A 提交）：</strong> 事务 A 完成修改，也执行 <strong>无版本检查</strong> 的 <code>UPDATE</code> 语句。</li>
</ol>
<p><strong>结果：</strong> 事务 B 的业务变更被事务 A 的修改覆盖，导致 <strong>丢失更新（Lost Update）</strong> 的竞态条件。</p>
<h3>乐观锁的解决之道：单次原子操作</h3>
<p>要解决这个问题，必须让 <strong>“检查旧版本”</strong> 和 <strong>“设置新值”</strong> 成为一个原子操作，即在 <code>UPDATE</code> 语句中加入版本过滤条件：</p>
<pre><code>UPDATE records
SET title = '新标题', version = version + 1
WHERE id = 1 AND version = 1; -- 关键：只有旧版本为 1 时才允许更新
</code></pre>
<p>在 SeaORM 中，我们使用 <code>update_many()</code> 配合 <code>filter()</code> 来构造这个原子操作，并通过检查 <code>rows_affected</code> 来判断操作是否成功。</p>
<hr>
<h2>三、Upsert 流程的抉择：先 Update 再 Insert 的优势</h2>
<p>实现 Upsert 功能主要有两种策略：<strong>“先 Update 再 Insert”</strong> 和 <strong>“先 Insert 再 Update”</strong>。在涉及<strong>乐观锁</strong>的业务中，<strong>“先 Update 再 Insert”</strong> 模式是更优的选择。</p>
<h3>1. 模式一：先 Update 再 Insert（推荐）</h3>
<p>这种模式总是优先处理最常见的情况：<strong>更新现有记录</strong>。</p>
<p><strong>优势分析：</strong></p>
<ul>
<li><strong>天然支持乐观锁：</strong> 乐观锁检查（<code>WHERE version = ?</code>）直接集成在 <code>UPDATE</code> 语句中，利用了数据库的原子性，保证了在单次操作中完成检查和修改。</li>
<li><strong>高效处理更新：</strong> 在高并发的更新场景中，大部分操作都是更新。这种模式只需执行一次成功的 <code>UPDATE</code> 就能完成任务，避免了不必要的 <code>INSERT</code> 尝试。</li>
</ul>
<h3>2. 模式二：先 Insert 再 Update</h3>
<p><strong>流程：</strong> 尝试 <code>INSERT</code> $\to$ 如果失败（主键冲突），执行 <code>UPDATE</code>。</p>
<p><strong>劣势分析：</strong></p>
<ul>
<li><strong>乐观锁实现复杂：</strong> 如果 <code>INSERT</code> 失败，转到 <code>UPDATE</code> 时，必须确保 <code>UPDATE</code> 操作是带有乐观锁检查的，这增加了流程的复杂性。</li>
<li><strong>高更新场景效率低：</strong> 如果大部分操作是更新，这种模式会强制执行一次注定会失败的 <code>INSERT</code> 操作（抛出主键冲突错误），然后再执行一次 <code>UPDATE</code>，浪费了数据库资源。</li>
</ul>
<h3>总结：选择 “先 Update 再 Insert” 的理由</h3>
<p>在处理带有乐观锁的聚合根持久化时，<strong>“先 Update 再 Insert”</strong> 模式是首选方案。它能够利用 <code>UPDATE</code> 的原子性高效地处理最常见的<strong>更新</strong>操作，并<strong>天然地</strong>将乐观锁检查与数据库写操作绑定。</p>
<hr>
<h2>四、SeaORM 中的 Upsert 流程：UPDATE $\to$ FIND $\to$ INSERT</h2>
<p>基于 <strong>“先 Update 再 Insert”</strong> 的策略，我们构建一个清晰的 <strong>"原子 UPDATE + FIND + INSERT"</strong> 三步流程，以可靠地处理成功更新、并发冲突和成功插入三种情况。</p>
<h3>核心实现代码</h3>
<pre><code>// 假设 entity.version 是更新后的新版本，expected_old_version = entity.version - 1
async fn save&lt;T: TransactionContext&gt;(
    &amp;self,
    callback: &amp;mut EventSourcedEntity&lt;Callback&gt;,
    txn: &amp;mut T,
) -&gt; Result&lt;(), RepositoryError&gt; {
    let conn = txn.get_connection();
    let entity: &amp;Callback = callback;
    let id = entity.channel.0.clone(); 
    let expected_old_version = entity.version - 1; 

    // 准备 ActiveModel，设置新的 version
    let mut active_model_for_update: ActiveModel = entity.clone().into_active_model();
    active_model_for_update.version = Set(entity.version); 

    // ----------------------------------------------------
    // 第一步：尝试原子 UPDATE（带乐观锁）
    // ----------------------------------------------------
    let res = callback_model::Entity::update_many()
        .set(active_model_for_update)
        .filter(callback_model::Column::Channel.eq(id.clone())) 
        .filter(callback_model::Column::Version.eq(expected_old_version)) // 乐观锁检查
        .exec(conn)
        .await?;

    if res.rows_affected &gt; 0 {
        // 更新成功：影响行数 &gt; 0，说明乐观锁条件满足。
        callback.move_event_to_context(txn);
        return Ok(());
    }

    // ----------------------------------------------------
    // 第二步：UPDATE 失败。使用 FIND 检查记录是否存在（判断是否为并发冲突）
    // ----------------------------------------------------
    if callback_model::Entity::find_by_id(id.clone())
        .one(conn)
        .await?
        .is_some()
    {
        // 记录存在。UPDATE 失败且记录存在，必然是版本不匹配，即并发冲突。
        return Err(RepositoryError::optimistic_lock_error(
            "Optimistic lock conflict: Record exists, but old version did not match."
        ));
    }

    // ----------------------------------------------------
    // 第三步：记录不存在，尝试 INSERT
    // ----------------------------------------------------
    let active_model_for_insert: ActiveModel = entity.clone().into_active_model();
    
    active_model_for_insert.insert(conn).await
        .map_err(|e| {
             // 如果 INSERT 失败，则视为并发冲突（在 FIND 之后被其他事务插入）。
             match e {
                 DbErr::RecordNotInserted | DbErr::Custom(_) =&gt; RepositoryError::optimistic_lock_error(
                    "Concurrency conflict: Record inserted after non-existence check."
                 ),
                 _ =&gt; e.into(),
            }
        })?;

    callback.move_event_to_context(txn);
    Ok(())
}
</code></pre>
<hr>
<h2>结论：告别竞态，拥抱原子性</h2>
<p>通过本文的分析和实践，我们可以得出以下关键结论：</p>
<ol>
<li><strong>乐观锁是高并发的基石：</strong> 放弃“先查后改”的传统模式，将<strong>版本检查</strong>与<strong>数据修改</strong>集成到一次原子性的 <code>UPDATE</code> 操作中，是避免丢失更新等竞态条件的根本方法。</li>
<li><strong>选择正确的 Upsert 策略：</strong> <strong>“先 Update 再 Insert”</strong> 模式凭借其对乐观锁的天然支持和对更新操作的高效处理，成为处理聚合根持久化的首选。</li>
<li><strong>利用数据库的原子性：</strong> 无论是通过检查 <code>rows_affected</code>，还是依赖主键约束错误来区分更新失败的原因，都是在充分利用数据库底层机制来确保数据一致性。</li>
</ol>
<p>在您的 Rust DDD/CQRS 架构中，将这种原子化逻辑封装进仓储（Repository）层的 <code>save()</code> 方法中，是确保数据完整性和系统高可用性的关键。</p>
]]></description><pubDate>2025-11-18 12:33:11</pubDate></item><item><title>with_err_location：让 Rust 错误处理更智能的过程宏</title><link>https://rustcc.cn/article?id=2650d510-e3ee-4f14-9284-5927ea273e91</link><description><![CDATA[<p>在 Rust 错误处理中，我们经常需要记录错误发生的位置信息以便调试。虽然 <code>snafu</code> 库提供了强大的错误处理能力，但手动为每个错误变体添加位置字段和工厂方法仍然繁琐且容易出错。本文介绍一个自定义的过程宏 <code>#[with_err_location]</code>，它可以自动化这些重复工作，让错误处理更加优雅和高效。</p>
<h2>问题背景</h2>
<p>使用 <code>snafu</code> 进行错误处理时，我们通常需要：</p>
<ol>
<li>为每个错误变体手动添加 <code>location</code> 字段</li>
<li>添加相应的属性（<code>#[snafu(implicit)]</code>、<code>#[serde(skip)]</code>）</li>
<li>为复杂的 source 字段添加 <code>#[snafu(source(false))]</code></li>
<li>手动实现工厂方法来创建错误实例</li>
</ol>
<p>这导致了大量的样板代码：</p>
<pre><code>#[derive(Debug, Serialize, Snafu)]
#[serde(tag = "type")]
pub enum ApiError {
    #[serde(rename = "validate_error")]
    ValidateError {
        message: String,
        #[serde(skip)]
        #[snafu(implicit)]
        location: snafu::Location,
    },
    
    #[serde(rename = "internal_error")]
    InternalError {
        message: String,
        #[serde(skip)]
        #[snafu(source(false))]
        source: Option&lt;Box&lt;dyn std::error::Error + Send + Sync&gt;&gt;,
        #[serde(skip)]
        #[snafu(implicit)]
        location: snafu::Location,
    },
}

impl ApiError {
    #[track_caller]
    pub fn validate_error(message: String) -&gt; Self {
        ApiError::ValidateError {
            message,
            location: GenerateImplicitData::generate(),
        }
    }
    
    #[track_caller]
    pub fn internal_error(message: String) -&gt; Self {
        ApiError::InternalError {
            message,
            source: None,
            location: GenerateImplicitData::generate(),
        }
    }
    
    #[track_caller]
    pub fn internal_error_with_source(message: String, source: Option&lt;Box&lt;dyn std::error::Error + Send + Sync&gt;&gt;) -&gt; Self {
        ApiError::InternalError {
            message,
            source,
            location: GenerateImplicitData::generate(),
        }
    }
}
</code></pre>
<h2>解决方案：<code>#[with_err_location]</code> 宏</h2>
<p><code>#[with_err_location]</code> 宏可以自动化所有这些工作，让您只需要定义核心的错误结构：</p>
<pre><code>#[with_err_location]
#[derive(Debug, Serialize, Snafu)]
#[serde(tag = "type")]
pub enum ApiError {
    #[serde(rename = "validate_error")]
    ValidateError {
        message: String,
    },
    
    #[serde(rename = "internal_error")]
    InternalError {
        message: String,
        source: Option&lt;Box&lt;dyn std::error::Error + Send + Sync&gt;&gt;,
    },
}
</code></pre>
<h2>核心特性</h2>
<h3>1. 自动添加 Location 字段</h3>
<p>宏会为每个枚举变体自动添加 <code>location: snafu::Location</code> 字段，并配置必要的属性：</p>
<ul>
<li><code>#[snafu(implicit)]</code>：让 snafu 自动填充位置信息</li>
<li><code>#[serde(skip)]</code>：在序列化时跳过该字段（默认行为）</li>
</ul>
<h3>2. 智能 Source 字段处理</h3>
<p>宏能识别复杂的 source 字段类型，并自动添加 <code>#[snafu(source(false))]</code> 属性：</p>
<pre><code>// 自动识别并处理
source: Option&lt;Box&lt;dyn std::error::Error + Send + Sync&gt;&gt;
</code></pre>
<h3>3. 自动生成工厂方法</h3>
<p>宏为每个变体生成相应的工厂方法：</p>
<h4>普通变体</h4>
<pre><code>// 生成：
pub fn validate_error(message: String) -&gt; Self { ... }
</code></pre>
<h4>复杂 Source 字段变体</h4>
<p>对于包含 <code>Option&lt;Box&lt;dyn Error + Send + Sync&gt;&gt;</code> 类型的 source 字段，宏会生成两个方法：</p>
<pre><code>// 基础方法（source = None）
pub fn internal_error(message: String) -&gt; Self { ... }

// 带 source 的方法
pub fn internal_error_with_source(message: String, source: Option&lt;Box&lt;dyn std::error::Error + Send + Sync&gt;&gt;) -&gt; Self { ... }
</code></pre>
<h3>4. 灵活的配置选项</h3>
<h4>全局配置</h4>
<pre><code>#[with_err_location(serde = true)]  // 不添加 #[serde(skip)]
#[derive(Debug, Snafu)]
pub enum ApiError { ... }
</code></pre>
<h4>变体级别配置</h4>
<pre><code>#[with_err_location]
#[derive(Debug, Snafu)]
pub enum ApiError {
    #[location(serde = true)]  // 此变体不添加 #[serde(skip)]
    SpecialError {
        message: String,
    },
}
</code></pre>
<h2>实现细节</h2>
<h3>宏的工作流程</h3>
<ol>
<li><strong>解析输入</strong>：解析枚举定义和宏参数</li>
<li><strong>字段分析</strong>：检查每个变体的字段类型和现有属性</li>
<li><strong>添加 Location 字段</strong>：为没有 location 字段的变体添加</li>
<li><strong>属性处理</strong>：添加必要的 snafu 和 serde 属性</li>
<li><strong>工厂方法生成</strong>：基于字段类型生成相应的工厂方法</li>
</ol>
<h3>关键函数</h3>
<h4>字段类型检测</h4>
<pre><code>fn should_add_source_false(field: &amp;syn::Field) -&gt; bool {
    let type_str = field.ty.to_token_stream().to_string();
    let is_option_box_dyn_error = type_str.starts_with("Option &lt; Box &lt; dyn");
    let is_source_field = field.ident.as_ref().map(|name| name == "source").unwrap_or(false);
    is_source_field &amp;&amp; is_option_box_dyn_error
}
</code></pre>
<h4>工厂方法生成</h4>
<pre><code>fn generate_factory_methods(input_enum: &amp;ItemEnum) -&gt; darling::Result&lt;TokenStream&gt; {
    // 检测复杂 source 字段
    let has_complex_source = fields_named.named.iter().any(should_add_source_false);
    
    if has_complex_source {
        // 生成两个方法：基础方法和带 source 的方法
    } else {
        // 生成单个方法
    }
}
</code></pre>
<h2>使用示例</h2>
<h3>基本使用</h3>
<pre><code>#[with_err_location]
#[derive(Debug, Snafu)]
pub enum MyError {
    NetworkError { url: String },
    ValidationError { field: String, message: String },
}

// 使用生成的工厂方法
let error = MyError::network_error("https://api.example.com".to_string());
</code></pre>
<h3>复杂 Source 字段</h3>
<pre><code>#[with_err_location]
#[derive(Debug, Snafu)]
pub enum ComplexError {
    DatabaseError {
        query: String,
        source: Option&lt;Box&lt;dyn std::error::Error + Send + Sync&gt;&gt;,
    },
}

// 两种使用方式
let error1 = ComplexError::database_error("SELECT * FROM users".to_string());
let error2 = ComplexError::database_error_with_source(
    "SELECT * FROM users".to_string(),
    Some(Box::new(io_error))
);
</code></pre>
<h3>配置选项</h3>
<pre><code>#[with_err_location(serde = true)]  // 全局配置
#[derive(Debug, Snafu)]
pub enum ApiError {
    #[location(serde = false)]  // 变体级别覆盖
    InternalError { message: String },
    
    PublicError { message: String },  // 使用全局配置
}
</code></pre>
<h2>完整代码</h2>
<pre><code>#[proc_macro_attribute]
pub fn with_err_location(
    args: proc_macro::TokenStream,
    input: proc_macro::TokenStream,
) -&gt; proc_macro::TokenStream {
    let args = args.into();
    with_err_location::with_err_location_impl(args, input.into())
        .unwrap_or_else(darling::Error::write_errors)
        .into()
} 
</code></pre>
<pre><code>use darling::{Error, FromMeta, ast::NestedMeta};
use proc_macro2::TokenStream;
use quote::{ToTokens, quote};
use syn::{Attribute, Field, Fields, ItemEnum, Meta, punctuated::Punctuated, token::Comma};

#[derive(Debug, FromMeta, Default)]
struct WithErrLocationArgs {
    pub serde: bool,
}

pub fn with_err_location_impl(
    args: TokenStream,
    input: TokenStream,
) -&gt; darling::Result&lt;TokenStream&gt; {
    let mut input_enum: ItemEnum = match syn::parse2(input) {
        Ok(v) =&gt; v,
        Err(e) =&gt; return Err(Error::from(e)),
    };

    // 解析全局参数
    let global_args = if args.is_empty() {
        WithErrLocationArgs::default()
    } else {
        let attr_args = match NestedMeta::parse_meta_list(args) {
            Ok(v) =&gt; v,
            Err(e) =&gt; return Err(Error::from(e)),
        };
        WithErrLocationArgs::from_list(&amp;attr_args).unwrap_or_default()
    };

    // 遍历枚举的所有变体
    for variant in &amp;mut input_enum.variants {
        // 查找并解析 #[location(...)] 属性
        let (location_config, remaining_attrs) =
            parse_and_remove_location_attrs(&amp;variant.attrs, &amp;global_args)?;

        // 移除 location 属性，保留其他属性
        variant.attrs = remaining_attrs;

        match &amp;mut variant.fields {
            Fields::Named(fields_named) =&gt; {
                // 检查是否已经有 location 字段
                let location_field_index = fields_named.named.iter().position(|field| {
                    field
                        .ident
                        .as_ref()
                        .map(|ident| ident == "location")
                        .unwrap_or(false)
                });
                match location_field_index {
                    Some(index) =&gt; {
                        // 如果已经有 location 字段，确保它至少有 #[snafu(implicit)]
                        let existing_field = &amp;mut fields_named.named[index];
                        ensure_location_field_has_snafu_implicit(existing_field, &amp;location_config);
                    }
                    None =&gt; {
                        // 如果没有 location 字段，则添加一个新的（总是带有 #[snafu(implicit)]）
                        let location_field = create_location_field(&amp;location_config);
                        fields_named.named.push(location_field);
                        fields_named.named.push_punct(Comma::default());
                    }
                }
                // 如果有source 且类型是Option&lt;Box&lt;dyn std::error::Error + Send + Sync&gt;&gt;
                // 需要为其加上#[snafu(source(false))]
                for field in &amp;mut fields_named.named {
                    if should_add_source_false(field) {
                        ensure_source_false_attribute(field);
                    }
                }
            }
            _ =&gt; {
                return Err(Error::unsupported_format(
                    "Only named fields variants are supported",
                ));
            }
        }
    }

    // 生成工厂方法
    let factory_methods = generate_factory_methods(&amp;input_enum)?;

    Ok(quote! {
        #input_enum
        #factory_methods
    })
}

/// 解析并移除 location 属性，返回配置和剩余属性
fn parse_and_remove_location_attrs(
    variant_attrs: &amp;[Attribute],
    global_args: &amp;WithErrLocationArgs,
) -&gt; darling::Result&lt;(LocationConfig, Vec&lt;Attribute&gt;)&gt; {
    let mut config = LocationConfig {
        serde: global_args.serde,
    };

    let mut remaining_attrs = Vec::new();

    for attr in variant_attrs {
        if attr.path().is_ident("location") {
            // 解析 location 属性的参数
            match &amp;attr.meta {
                Meta::List(meta_list) =&gt; {
                    let nested = meta_list.parse_args_with(
                        Punctuated::&lt;NestedMeta, syn::Token![,]&gt;::parse_terminated,
                    )?;
                    let location_args =
                        WithErrLocationArgs::from_list(&amp;nested.into_iter().collect::&lt;Vec&lt;_&gt;&gt;())?;

                    config.serde = location_args.serde;
                }
                _ =&gt; {
                    // 如果没有参数，使用默认配置
                }
            }
        } else {
            // 保留非 location 属性
            remaining_attrs.push(attr.clone());
        }
    }

    Ok((config, remaining_attrs))
}

#[derive(Debug)]
struct LocationConfig {
    serde: bool,
}

/// 确保现有的 location 字段至少有 #[snafu(implicit)] 属性
fn ensure_location_field_has_snafu_implicit(field: &amp;mut Field, config: &amp;LocationConfig) {
    // 根据配置添加或确保有 #[serde(skip)]
    if !config.serde {
        let has_serde_skip = field.attrs.iter().any(|attr| {
            if attr.path().is_ident("serde")
                &amp;&amp; let Meta::List(meta_list) = &amp;attr.meta
            {
                return meta_list.tokens.to_string().contains("skip");
            }
            false
        });

        if !has_serde_skip {
            let serde_skip_attr: Attribute = syn::parse_quote! {
                #[serde(skip)]
            };
            field.attrs.push(serde_skip_attr);
        }
    }
    let has_snafu_implicit = field.attrs.iter().any(|attr| {
        if attr.path().is_ident("snafu")
            &amp;&amp; let Meta::List(meta_list) = &amp;attr.meta
        {
            return meta_list.tokens.to_string().contains("implicit");
        }
        false
    });

    // 如果没有 #[snafu(implicit)]，则添加它
    if !has_snafu_implicit {
        let snafu_implicit_attr: Attribute = syn::parse_quote! {
            #[snafu(implicit)]
        };
        field.attrs.push(snafu_implicit_attr);
    }
}

/// 检查字段是否需要自动添加 #[snafu(source(false))]
fn should_add_source_false(field: &amp;syn::Field) -&gt; bool {
    let type_str = field.ty.to_token_stream().to_string();

    // 检查是否是 Option&lt;Box&lt;dyn std::error::Error + Send + Sync&gt;&gt; 类型
    let is_option_box_dyn_error = type_str.starts_with("Option &lt; Box &lt; dyn");

    // 检查字段名是否为 "source"
    let is_source_field = field
        .ident
        .as_ref()
        .map(|name| name == "source")
        .unwrap_or(false);

    is_source_field &amp;&amp; is_option_box_dyn_error
}

/// 确保复杂 source 字段有 #[snafu(source(false))] 属性
fn ensure_source_false_attribute(field: &amp;mut Field) {
    // 检查是否已经有 #[snafu(source(false))] 属性
    let has_source_false = field.attrs.iter().any(|attr| {
        if attr.path().is_ident("snafu")
            &amp;&amp; let Meta::List(meta_list) = &amp;attr.meta
        {
            let tokens_str = meta_list.tokens.to_string();
            return tokens_str.contains("source")
                &amp;&amp; (tokens_str.contains("false") || tokens_str.contains("( false )"));
        }
        false
    });

    // 如果没有，则添加 #[snafu(source(false))]
    if !has_source_false {
        let source_false_attr: Attribute = syn::parse_quote! {
            #[snafu(source(false))]
        };
        field.attrs.push(source_false_attr);
    }
}

/// 根据配置创建 location 字段
fn create_location_field(config: &amp;LocationConfig) -&gt; Field {
    if !config.serde {
        syn::parse_quote! {
            #[serde(skip)]
            #[snafu(implicit)]
            location: snafu::Location
        }
    } else {
        syn::parse_quote! {
            #[snafu(implicit)]
            location: snafu::Location
        }
    }
}

/// 为枚举生成工厂方法
fn generate_factory_methods(input_enum: &amp;ItemEnum) -&gt; darling::Result&lt;TokenStream&gt; {
    let enum_name = &amp;input_enum.ident;
    let mut methods = Vec::new();

    for variant in &amp;input_enum.variants {
        let variant_name = &amp;variant.ident;

        // 将变体名转换为 snake_case
        let method_name = convert_to_snake_case(&amp;variant_name.to_string());
        let method_ident = syn::Ident::new(&amp;method_name, variant_name.span());

        match &amp;variant.fields {
            Fields::Named(fields_named) =&gt; {
                // 检查是否有复杂的 source 字段
                let has_complex_source = fields_named.named.iter().any(should_add_source_false);

                if has_complex_source {
                    // 生成两个方法：基础方法（source = None）和带 source 的方法

                    // 1. 基础方法：source 为 None
                    let (base_params, base_assignments) =
                        analyze_fields_for_source_method(fields_named, true);
                    let base_method = quote! {
                        #[track_caller]
                        pub fn #method_ident(#(#base_params),*) -&gt; Self {
                            #enum_name::#variant_name {
                                #(#base_assignments,)*
                            }
                        }
                    };
                    methods.push(base_method);

                    // 2. 带 source 的方法
                    let source_method_name = format!("{}_with_source", method_name);
                    let source_method_ident =
                        syn::Ident::new(&amp;source_method_name, variant_name.span());
                    let (source_params, source_assignments) =
                        analyze_fields_for_source_method(fields_named, false);

                    let source_method = quote! {
                        #[track_caller]
                        pub fn #source_method_ident(#(#source_params),*) -&gt; Self
                        {
                            #enum_name::#variant_name {
                                #(#source_assignments,)*
                            }
                        }
                    };
                    methods.push(source_method);
                } else {
                    // 分析字段，确定需要的参数
                    let (params, field_assignments) = analyze_fields(fields_named);

                    // 生成基础方法
                    let method = quote! {
                        #[track_caller]
                        pub fn #method_ident(#(#params),*) -&gt; Self {
                            #enum_name::#variant_name {
                                #(#field_assignments,)*
                            }
                        }
                    };

                    methods.push(method);
                }
            }
            _ =&gt; continue,
        }
    }

    Ok(quote! {
        impl #enum_name {
            #(#methods)*
        }
    })
}

/// 将 PascalCase 转换为 snake_case
fn convert_to_snake_case(s: &amp;str) -&gt; String {
    let mut result = String::new();
    for (i, ch) in s.chars().enumerate() {
        if ch.is_uppercase() &amp;&amp; i &gt; 0 {
            result.push('_');
        }
        result.push(ch.to_lowercase().next().unwrap());
    }
    result
}

/// 分析字段，生成参数和字段赋值
fn analyze_fields(fields: &amp;syn::FieldsNamed) -&gt; (Vec&lt;TokenStream&gt;, Vec&lt;TokenStream&gt;) {
    let mut params = Vec::new();
    let mut assignments = Vec::new();

    for field in &amp;fields.named {
        let field_name = field.ident.as_ref().unwrap();
        let field_type = &amp;field.ty;

        if field_name == "location" {
            assignments.push(quote! { #field_name: snafu::GenerateImplicitData::generate() });
            continue;
        }

        // 普通字段作为参数
        params.push(quote! { #field_name: #field_type });
        assignments.push(quote! { #field_name });
    }

    (params, assignments)
}

/// 分析字段，为带 source 的方法生成参数和字段赋值
fn analyze_fields_for_source_method(
    fields: &amp;syn::FieldsNamed,
    is_base: bool,
) -&gt; (Vec&lt;TokenStream&gt;, Vec&lt;TokenStream&gt;) {
    let mut params = Vec::new();
    let mut assignments = Vec::new();

    for field in &amp;fields.named {
        let field_name = field.ident.as_ref().unwrap();
        let field_type = &amp;field.ty;

        if field_name == "location" {
            assignments.push(quote! { #field_name: snafu::GenerateImplicitData::generate() });
            continue;
        }

        if is_base &amp;&amp; should_add_source_false(field) {
            // 复杂 source 字段设为 None，不作为参数
            assignments.push(quote! { #field_name: None });
        } else {
            // 普通字段作为参数
            params.push(quote! { #field_name: #field_type });
            assignments.push(quote! { #field_name });
        }
    }

    (params, assignments)
}

</code></pre>
<h2>优势总结</h2>
<ol>
<li><strong>减少样板代码</strong>：自动生成重复的字段和方法定义</li>
<li><strong>类型安全</strong>：在编译时确保正确的类型处理</li>
<li><strong>灵活配置</strong>：支持全局和变体级别的配置选项</li>
<li><strong>智能处理</strong>：自动识别复杂类型并生成相应的方法</li>
<li><strong>向后兼容</strong>：可以与现有的 snafu 代码无缝集成</li>
</ol>
<h2>结论</h2>
<p><code>#[with_err_location]</code> 宏通过自动化错误处理中的重复工作，显著提升了开发效率和代码质量。它不仅减少了样板代码，还通过智能的类型检测和方法生成，提供了更加优雅和类型安全的错误处理解决方案。</p>
<p>无论是简单的错误类型还是复杂的带源错误的场景，这个宏都能提供恰到好处的自动化支持，让开发者能够专注于业务逻辑而不是重复的错误处理代码。</p>
]]></description><pubDate>2025-11-18 12:31:08</pubDate></item><item><title>Rust 中方法名到 SQL 语句的智能解析与构造</title><link>https://rustcc.cn/article?id=94e94dd2-2549-44b9-b884-da2655b1d5eb</link><description><![CDATA[<p>在构建任何复杂的应用程序时，数据持久化都是核心环节。通常会采用**仓储模式（Repository Pattern）**来解耦业务逻辑和数据访问逻辑。这带来了清晰的架构，但也引入了一个常见的问题：大量的样板代码（Boilerplate Code）。</p>
<p>每个实体都需要一个仓储接口，以及对应的实现。<code>find_by_id</code>, <code>find_by_email</code>, <code>exists_by_name</code>, <code>delete_by_id</code>... 这些简单却重复的方法，我们一遍又一遍地编写，不仅枯燥，还容易出错。</p>
<h3>痛点分析：传统仓储层的重复劳动</h3>
<p>下面是 <code>UserRepository</code> 在没有自动化工具时的完整面貌：</p>
<p><strong>1. Trait 定义 - 接口膨胀</strong></p>
<pre><code>// src/domain/repositories/user_repository.rs
pub trait UserRepository {
    // 基础CRUD
    async fn find_by_id(&amp;self, txn: &amp;mut Txn, id: &amp;UserId) -&gt; Result&lt;Option&lt;User&gt;&gt;;
    async fn save(&amp;self, txn: &amp;mut Txn, user: &amp;mut User) -&gt; Result&lt;()&gt;;
    async fn delete_by_id(&amp;self, txn: &amp;mut Txn, id: &amp;UserId) -&gt; Result&lt;()&gt;;
    
    // 各种查询方法
    async fn find_id_by_email(&amp;self, txn: &amp;mut Txn, email: &amp;Email) -&gt; Result&lt;Option&lt;UserId&gt;&gt;;
    async fn find_id_by_user_name(&amp;self, txn: &amp;mut Txn, user_name: &amp;str) -&gt; Result&lt;Option&lt;UserId&gt;&gt;;
    async fn exists_by_email(&amp;self, txn: &amp;mut Txn, email: &amp;Email) -&gt; Result&lt;bool&gt;;
    async fn find_by_status(&amp;self, txn: &amp;mut Txn, status: UserStatus) -&gt; Result&lt;Vec&lt;User&gt;&gt;;
    // ... 更多类似方法，接口越来越庞大
}
</code></pre>
<p><strong>2. 实现类 - 重复的SQL模板</strong></p>
<pre><code>// src/infrastructure/repositories_impl/user_repository_impl.rs
pub struct UserRepositoryImpl;

#[async_trait::async_trait]
impl UserRepository for UserRepositoryImpl {
    async fn find_by_id(&amp;self, txn: &amp;mut Txn, id: &amp;UserId) -&gt; Result&lt;Option&lt;User&gt;&gt; {
        // 每个方法都要写几乎相同的模板代码
        txn.query_sql_one("SELECT * FROM ua_user WHERE id = $1", [id.into()]).await
    }

    async fn find_id_by_email(&amp;self, txn: &amp;mut Txn, email: &amp;Email) -&gt; Result&lt;Option&lt;UserId&gt;&gt; {
        // 只是表名、字段名、参数位置变化，结构完全一样
        txn.query_sql_one("SELECT id FROM ua_user WHERE email = $1", [email.into()]).await
    }

    async fn find_id_by_user_name(&amp;self, txn: &amp;mut Txn, user_name: &amp;str) -&gt; Result&lt;Option&lt;UserId&gt;&gt; {
        // 重复第三次...
        txn.query_sql_one("SELECT id FROM ua_user WHERE user_name = $1", [user_name.into()]).await
    }

    async fn exists_by_email(&amp;self, txn: &amp;mut Txn, email: &amp;Email) -&gt; Result&lt;bool&gt; {
        // 稍微复杂一点，但模式仍然固定
        txn.query_sql_one("SELECT EXISTS(SELECT 1 FROM ua_user WHERE email = $1)", [email.into()]).await
    }
    
    // 保存逻辑更复杂，但也是固定模式
    async fn save(&amp;self, txn: &amp;mut Txn, user: &amp;mut User) -&gt; Result&lt;()&gt; {
        if user.is_new() {
            // INSERT 逻辑
            let sql = "INSERT INTO ua_user (id, email, user_name, ...) VALUES ($1, $2, $3, ...)";
            txn.execute_sql(sql, params![user.id(), user.email(), ...]).await?;
        } else {
            // UPDATE 逻辑  
            let sql = "UPDATE ua_user SET email = $1, user_name = $2, ... WHERE id = $3";
            txn.execute_sql(sql, params![user.email(), user.user_name(), user.id()]).await?;
        }
        Ok(())
    }
}
</code></pre>
<p><strong>3. 测试类 - 更多的重复</strong></p>
<pre><code>// tests/user_repository_test.rs
// 还需要为每个方法编写测试，Mock 各种依赖...
</code></pre>
<p>这种模式的问题很明显：</p>
<ul>
<li><strong>代码重复</strong>：每个查询方法都是相似的模板</li>
<li><strong>维护困难</strong>：表结构变更需要修改多个地方</li>
<li><strong>容易出错</strong>：手写SQL容易有拼写错误</li>
<li><strong>效率低下</strong>：花费大量时间在机械性编码上</li>
</ul>
<h3>✨ 解决方案：<code>#[repository]</code> 宏的威力</h3>
<p>现在让我们看看使用 <code>#[repository]</code> 宏之后的变化：</p>
<pre><code>// src/domain/repositories/user_repository.rs
use core_common::repository;

#[repository(aggregate = User, table_name = "ua_user")]
pub trait UserRepository: Send + Sync {
    // 复杂的、需要特殊逻辑的查询，保持手写实现
    async fn get_all_permissions(
        &amp;self,
        user_id: &amp;UserId,
        tenant_id: Option&lt;TenantId&gt;,
    ) -&gt; Result&lt;PermissionIdHashSet, RepositoryError&gt; {
        // 这里仍然可以手写复杂实现
        // 比如联表查询、特殊业务逻辑等
    }

    // 简单查询：只需定义方法签名，空函数体 {}
    // 宏会自动生成完整的SQL实现！
    
    // 根据ID查询
    async fn find_by_id(&amp;self, id: &amp;UserId) -&gt; Result&lt;Option&lt;User&gt;, RepositoryError&gt; {}
    
    // 根据各种字段查询ID
    async fn find_id_by_user_name(&amp;self, user_name: &amp;str) -&gt; Result&lt;Option&lt;UserId&gt;, RepositoryError&gt; {}
    async fn find_id_by_email(&amp;self, email: &amp;Email) -&gt; Result&lt;Option&lt;UserId&gt;, RepositoryError&gt; {}
    async fn find_id_by_phone(&amp;self, phone: &amp;Phone) -&gt; Result&lt;Option&lt;UserId&gt;, RepositoryError&gt; {}
    
    // 存在性检查
    async fn exists_by_email(&amp;self, email: &amp;Email) -&gt; Result&lt;bool, RepositoryError&gt; {}
    async fn exists_by_user_name(&amp;self, user_name: &amp;str) -&gt; Result&lt;bool, RepositoryError&gt; {}
    
    // 复杂条件查询
    async fn find_by_email_and_status(&amp;self, email: &amp;Email, status: UserStatus) -&gt; Result&lt;Vec&lt;User&gt;, RepositoryError&gt; {}
    async fn find_by_email_or_phone(&amp;self, email: &amp;Email, phone: &amp;Phone) -&gt; Result&lt;Vec&lt;User&gt;, RepositoryError&gt; {}
    
    // 统计查询
    async fn count_by_status(&amp;self, status: UserStatus) -&gt; Result&lt;i64, RepositoryError&gt; {}
    
    // 删除操作
    async fn delete_by_id(&amp;self, id: &amp;UserId) -&gt; Result&lt;(), RepositoryError&gt; {}
    
    // 保存操作 - 宏会自动处理INSERT/UPDATE逻辑
    async fn save(&amp;self, user: &amp;mut User) -&gt; Result&lt;(), RepositoryError&gt; {}
}
</code></pre>
<p><strong>宏的魔法效果详解：</strong></p>
<ol>
<li>
<p><strong>自动实现生成</strong></p>
<ul>
<li>编译时自动为每个空方法生成完整的SQL实现</li>
<li>无需手动编写 <code>UserRepositoryImpl</code></li>
<li>生成的代码与手写代码质量相当，但零错误</li>
</ul>
</li>
<li>
<p><strong>智能参数注入</strong></p>
<ul>
<li>自动添加事务参数 <code>txn: &amp;mut TC</code></li>
<li>自动处理参数类型转换（如 <code>Email</code> → 数据库类型）</li>
<li>最终方法签名实际上是：<code>find_id_by_email(&amp;self, txn: &amp;mut TC, email: &amp;Email)</code></li>
</ul>
</li>
<li>
<p><strong>CRUD 功能继承</strong></p>
<ul>
<li>通过 <code>aggregate = User</code> 自动继承 <code>CrudRepository&lt;User, TC&gt;</code></li>
<li>免费获得：<code>find_by_id</code>, <code>save</code>, <code>delete_by_id</code>, <code>exists_by_id</code> 等标准方法</li>
<li>无需重复定义基础CRUD操作</li>
</ul>
</li>
<li>
<p><strong>完整的测试支持</strong></p>
<ul>
<li>自动利用 <code>mockall</code> 生成Trait的Mock实现</li>
<li>在测试中可以直接：<code>let mut mock = MockUserRepository::new();</code></li>
<li>极大简化单元测试的编写</li>
</ul>
</li>
</ol>
<h3>🧙♂️ 魔法揭秘：命名约定解析引擎</h3>
<p>宏的核心是一个强大的方法名解析器，它将自然语言风格的方法名转换为精确的SQL查询。</p>
<h4>查询类型推断规则</h4>
<table>
<thead>
<tr>
<th>方法名前缀</th>
<th>生成的 SQL 类型</th>
<th>返回类型</th>
<th>示例</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>find_by_...</code></td>
<td><code>SELECT *</code></td>
<td><code>Option&lt;T&gt;</code> 或 <code>Vec&lt;T&gt;</code></td>
<td><code>find_by_email</code></td>
</tr>
<tr>
<td><code>find_{field}_by_...</code></td>
<td><code>SELECT {field}</code></td>
<td><code>Option&lt;FieldType&gt;</code></td>
<td><code>find_id_by_email</code></td>
</tr>
<tr>
<td><code>find_{field1}_and_{field2}_by_...</code></td>
<td><code>SELECT {field1}, {field2}</code></td>
<td>元组或自定义结构体</td>
<td><code>find_id_and_email_by_phone</code></td>
</tr>
<tr>
<td><code>exists_by_...</code></td>
<td><code>SELECT EXISTS(...)</code></td>
<td><code>bool</code></td>
<td><code>exists_by_email</code></td>
</tr>
<tr>
<td><code>count_by_...</code></td>
<td><code>SELECT COUNT(*)</code></td>
<td><code>i64</code></td>
<td><code>count_by_status</code></td>
</tr>
<tr>
<td><code>delete_by_...</code></td>
<td><code>DELETE</code></td>
<td><code>()</code></td>
<td><code>delete_by_id</code></td>
</tr>
</tbody>
</table>
<h4>条件运算符映射</h4>
<table>
<thead>
<tr>
<th>方法名后缀</th>
<th>SQL 运算符</th>
<th>示例</th>
<th>生成 WHERE 子句</th>
</tr>
</thead>
<tbody>
<tr>
<td>(无后缀)</td>
<td><code>=</code></td>
<td><code>find_by_email</code></td>
<td><code>email = $1</code></td>
</tr>
<tr>
<td><code>_not</code></td>
<td><code>!=</code></td>
<td><code>find_by_status_not</code></td>
<td><code>status != $1</code></td>
</tr>
<tr>
<td><code>_like</code></td>
<td><code>LIKE</code></td>
<td><code>find_by_name_like</code></td>
<td><code>name LIKE $1</code></td>
</tr>
<tr>
<td><code>_gte</code></td>
<td><code>&gt;=</code></td>
<td><code>find_by_age_gte</code></td>
<td><code>age &gt;= $1</code></td>
</tr>
<tr>
<td><code>_lte</code></td>
<td><code>&lt;=</code></td>
<td><code>find_by_age_lte</code></td>
<td><code>age &lt;= $1</code></td>
</tr>
<tr>
<td><code>_between</code></td>
<td><code>BETWEEN</code></td>
<td><code>find_by_age_between</code></td>
<td><code>age BETWEEN $1 AND $2</code></td>
</tr>
<tr>
<td><code>_in</code></td>
<td><code>IN</code></td>
<td><code>find_by_status_in</code></td>
<td><code>status IN ($1, $2, ...)</code></td>
</tr>
<tr>
<td><code>_is_null</code></td>
<td><code>IS NULL</code></td>
<td><code>find_by_deleted_at_is_null</code></td>
<td><code>deleted_at IS NULL</code></td>
</tr>
</tbody>
</table>
<h4>逻辑连接符处理</h4>
<table>
<thead>
<tr>
<th>连接词</th>
<th>SQL 逻辑</th>
<th>示例</th>
<th>生成条件</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>_and_</code></td>
<td><code>AND</code></td>
<td><code>find_by_email_and_status</code></td>
<td><code>email = $1 AND status = $2</code></td>
</tr>
<tr>
<td><code>_or_</code></td>
<td><code>OR</code></td>
<td><code>find_by_email_or_phone</code></td>
<td><code>email = $1 OR phone = $2</code></td>
</tr>
</tbody>
</table>
<h4>排序和分页支持</h4>
<table>
<thead>
<tr>
<th>后缀</th>
<th>SQL 子句</th>
<th>示例</th>
<th>效果</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>_order_by_{field}_asc</code></td>
<td><code>ORDER BY field ASC</code></td>
<td><code>find_by_status_order_by_created_at_asc</code></td>
<td>按创建时间升序</td>
</tr>
<tr>
<td><code>_order_by_{field}_desc</code></td>
<td><code>ORDER BY field DESC</code></td>
<td><code>find_by_status_order_by_created_at_desc</code></td>
<td>按创建时间降序</td>
</tr>
<tr>
<td><code>_first</code> / <code>_top</code></td>
<td><code>LIMIT 1</code></td>
<td><code>find_by_status_first</code></td>
<td>只返回第一条</td>
</tr>
<tr>
<td><code>_limit_{n}</code></td>
<td><code>LIMIT n</code></td>
<td><code>find_by_status_limit_10</code></td>
<td>返回前10条</td>
</tr>
</tbody>
</table>
<h4>实际解析示例</h4>
<p><strong>简单查询：</strong></p>
<pre><code>async fn find_id_by_email(&amp;self, email: &amp;Email) -&gt; Result&lt;Option&lt;UserId&gt;&gt; {}
</code></pre>
<p>↓ 生成 SQL：</p>
<pre><code>SELECT id FROM "ua_user" WHERE email = $1
</code></pre>
<p><strong>复杂查询：</strong></p>
<pre><code>async fn find_id_by_email_and_status_order_by_created_at_desc(
    &amp;self, 
    email: &amp;Email, 
    status: UserStatus
) -&gt; Result&lt;Option&lt;UserId&gt;&gt; {}
</code></pre>
<p>↓ 生成 SQL：</p>
<pre><code>SELECT id FROM "ua_user" 
WHERE email = $1 AND status = $2 
ORDER BY created_at DESC
</code></pre>
<p><strong>存在性检查：</strong></p>
<pre><code>async fn exists_by_email_and_tenant_id(
    &amp;self, 
    email: &amp;Email, 
    tenant_id: &amp;TenantId
) -&gt; Result&lt;bool&gt; {}
</code></pre>
<p>↓ 生成 SQL：</p>
<pre><code>SELECT EXISTS(
    SELECT 1 FROM "ua_user" 
    WHERE email = $1 AND tenant_id = $2
)
</code></pre>
<h3>⚙️ 灵活的配置选项</h3>
<p>宏支持丰富的配置参数来适应不同场景：</p>
<pre><code>// 基础配置
#[repository(aggregate = User, table_name = "ua_user")]

// 多租户支持
#[repository(aggregate = User, table_name = "ua_user", tenant = true)]

// 禁用Mock生成（用于性能敏感场景）
#[repository(aggregate = User, table_name = "ua_user", mock = false)]

// 自定义事务上下文
#[repository(
    aggregate = User, 
    table_name = "ua_user", 
    context = "db: &amp;Db"  // 使用自定义的db参数而非默认txn
)]

// 复杂配置组合
#[repository(
    aggregate = User,
    table_name = "ua_user",
    tenant = true,
    mock = true,
    context = "conn: &amp;mut PgConnection"
)]
</code></pre>
<h3>🎯 最佳实践和适用场景</h3>
<h4>推荐使用宏的场景：</h4>
<ul>
<li><strong>简单CRUD操作</strong>：95%的数据库查询都适合</li>
<li><strong>标准查询模式</strong>：等值查询、范围查询、存在性检查等</li>
<li><strong>快速原型开发</strong>：快速验证业务逻辑，后期可替换为手写优化SQL</li>
<li><strong>团队规范统一</strong>：确保所有开发者使用一致的查询模式</li>
</ul>
<h4>建议手写实现的场景：</h4>
<ul>
<li><strong>复杂联表查询</strong>：涉及多个表的复杂JOIN操作</li>
<li><strong>自定义聚合查询</strong>：GROUP BY、HAVING等复杂聚合</li>
<li><strong>数据库特定优化</strong>：需要数据库特定语法或优化提示</li>
<li><strong>存储过程调用</strong>：调用数据库存储过程或函数</li>
</ul>
<h3>📊 实际效果对比</h3>
<p><strong>代码量减少：</strong></p>
<ul>
<li>传统方式：每个查询方法需要 5-10 行实现代码</li>
<li>宏方式：每个查询方法只需 1 行声明</li>
<li>节省比例：约 80-90% 的代码量</li>
</ul>
<p><strong>开发效率提升：</strong></p>
<ul>
<li>新查询方法：从 5分钟/个 减少到 30秒/个</li>
<li>维护成本：表结构变更时，只需修改宏参数，无需改动多个方法</li>
<li>错误率：编译时检查替代运行时SQL错误</li>
</ul>
<h3>总结</h3>
<p><code>#[repository]</code> 宏不仅仅是一个代码生成工具，它代表了一种<strong>声明式数据访问</strong>的新范式。通过将开发者的重心从"如何实现"转移到"想要什么"，它真正实现了：</p>
<ul>
<li><strong>⚡ 极致效率</strong>：声明即实现，开发速度提升5-10倍</li>
<li><strong>🔒 绝对安全</strong>：编译时检查确保所有查询的类型安全</li>
<li><strong>📚 规范统一</strong>：强制执行一致的代码标准和命名约定</li>
<li><strong>🧪 测试友好</strong>：开箱即用的Mock支持，测试编写效率大幅提升</li>
<li><strong>🛠 灵活演进</strong>：复杂场景仍可手写实现，兼顾简单与复杂需求</li>
</ul>
<p>在Rust强大的元编程能力支持下，我们能够构建出这样既智能又实用的开发工具。这不仅是技术的进步，更是开发体验的革新——让我们能够更专注于业务逻辑本身，而不是重复的机械性工作。</p>
]]></description><pubDate>2025-11-18 12:17:16</pubDate></item></channel></rss>