<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-07-29 SteelMC：Rust Minecraft 服务器区块生成速度达到原版 18.8 倍</title><link>https://rustcc.cn/article?id=448ea6ca-78a3-4872-8653-c3d9e16684fe</link><description><![CDATA[<h2>SteelMC：Rust Minecraft 服务器区块生成速度达到原版 18.8 倍</h2>
<p>这不是那种“刚建仓库就宣布重写世界”的 Rust 项目。作者把 <code>SteelMC</code> 悄悄做了九个月，等到它已经能让玩家真正连进来、加载持久化世界、和方块与背包交互、并把地形生成跑到 <strong>block-for-block 接近原版一致</strong>，才正式公开。最硬的一组数据，是它在 7500 个随机区块上的地形一致性测试，以及一台 <code>Ryzen 9 9950X</code> 上对 10201 个新区块的生成基准：Steel 平均每秒 2582 个区块，冷启动吞吐量约是 Fabric + Chunky 的 <strong>18.8 倍</strong>。</p>
<p>更值得 Rust 圈关注的，其实不是“更快”三个字，而是作者怎么把复杂世界生成问题拆成可以被系统化控制的并发流水线。文章详细讲了它如何把 Minecraft 的分阶段区块依赖重构成一个 <strong>chunk pyramid</strong> 调度模型，再把跨区块特性、光照传播、后台 IO、主线程 tick handoff 这些最容易互相卡住的部分拆开处理。甚至连 vanilla 里某些看似偶然、但已经被红石玩家当成特性的行为，它也明确选择“保留 bug-compatible 行为”，而不是为了整洁硬改规则。</p>
<p>这类项目的意义很大：它说明 Rust 在游戏后端里不只是“性能语言”，而是能把 <strong>并发调度、确定性、可维护的系统边界</strong> 一起往前推。对做多人游戏服务端、仿真系统、复杂状态机调度的人来说，这篇公开设计说明非常值得细读。</p>
<p>原文链接：https://steelmc.dev/blog/announcement/</p>
<h2>PidgeIoT：用 workers-rs + Dioxus 把 Rust 从云端一路铺到 IoT 控制台</h2>
<p><code>PidgeIoT</code> 这篇分享很有现实工程味。作者做的是一个开源 IoT 设备管理平台：设备注册、shadow 配置下发、可查询遥测历史、邮件告警、OTA 固件更新，一整套云侧实现都用 Rust 打通。技术栈也很有代表性：后端是跑在 Cloudflare Workers 上的 <code>workers-rs</code>，每个设备对应一个 Durable Object；前端则是 <code>Dioxus 0.7</code> 的 WASM SPA；中间再用一个共享 <code>serde</code> 类型 crate 把前后端数据结构绑在一起。</p>
<p>这里最值得抄作业的点，是作者把“Rust 前后端同构”讲得非常具体：数据库侧用 <code>*Row</code> 结构承载原生时间戳与存储类型，对外 API 再映射成 RFC 3339 等更友好的表示，中间靠 <code>From</code> 转换衔接。这样一来，加字段时编译器会直接把所有漏改的地方都揪出来，不需要 OpenAPI 代码生成，也没有 schema drift。除此之外，文章还很坦诚地复盘了 <code>workers-rs</code> 和 <code>Dioxus 0.7</code> 在生产环境里踩过的坑，比如 <code>SqlCursor::one()</code> 的异常行为、CORS 头拼接、SSG hydration 丢 query 参数、以及 <code>wasm-split</code> 看似漂亮但实际无法上线的问题。</p>
<p>设备认证设计也很漂亮：作者没有套 JWT，而是给每台设备分配独立 <code>Ed25519</code> 密钥对，token 只保留版本号、过期时间和签名，既省解析成本，也把“轮换即撤销”这件事做得很自然。对想用 Rust 同时覆盖边缘云函数、管理后台和设备平台的人来说，这篇经验帖参考价值很高。</p>
<p>原文链接：https://old.reddit.com/r/rust/comments/1v9f8zv/pidgeiot_an_opensource_iot_platform_in_rust_end/</p>
<h2>multicalc：把 no_std Rust 科学计算栈直接推到 1kHz 实时控制环</h2>
<p><code>multicalc</code> 这条很容易被低估。作者做的不是单点数学库，而是一整套面向 <strong>实时嵌入式系统</strong> 的科学计算组件：估计、控制、运动学、Lie 群、微积分、自动微分、线性代数，全部放在稳定版 <code>no_std</code> Rust 里，而且目标非常明确：<strong>无堆分配、无 panic、无 unsafe</strong>。为了证明这不是“API 看上去很完整”的纸面项目，作者直接给出了一个很硬的实战场景：2D 机器人先用 2000 粒子的粒子滤波在噪声激光雷达下做定位，再用 5 状态 EKF 融合轮速、IMU 和 GPS，最后用 Follow-the-Gap 控制器在障碍赛道上跑到 <strong>60 万 tick 零碰撞</strong>。</p>
<p>文章最吸引人的，是它把嵌入式 Rust 常见的几组矛盾压到了一起：既要高频控制，又要可验证；既要 no_std，又要覆盖足够多的数值方法；既要追求性能，又不愿为了“快一点”把 <code>unsafe</code> 和 panic 风险偷偷带进库边界。作者的做法是把所有类型固定尺寸、所有调用工作量做成有界，并且在 <code>x86_64</code>、<code>aarch64</code> 以及多种 bare-metal ABI 上持续测试，再拿 <code>numpy</code>、<code>scipy</code>、<code>filterpy</code> 的结果做近似 1 ulp 的对照校验。</p>
<p>如果你关心 Rust 在机器人、控制系统、嵌入式数值计算里的真实上限，这个项目很值得盯。它已经不只是“Rust 也能做一点控制论”，而是在尝试把一整层实时控制数学基础设施系统化地带进 Rust。</p>
<p>原文链接：https://old.reddit.com/r/rust/comments/1v82qx7/multicalc_scientific_computing_for_real_time/</p>
<h2>OMQ.rs：不靠 libzmq，纯 Rust 复刻一整套 ZeroMQ 架构与传输栈</h2>
<p><code>OMQ.rs</code> 这条很对基础设施工程师胃口。作者想做的不是“给 ZeroMQ 包一层 Rust binding”，而是把 libzmq 那套使用体验和架构风格，<strong>原生重做成纯 Rust 实现</strong>：同样的 socket model，同样的 connect / bind / send / recv 形状，支持 connect-before-bind、自动重连、多传输透明切换，同时不再依赖 C 编译器和 libzmq 本体。</p>
<p>从当前能力看，这已经不是玩具了：项目覆盖了 20 种 socket 类型、9 种传输方式，安全层支持 <code>NULL</code>、<code>PLAIN</code>、<code>CURVE</code>，既有异步 socket，也有阻塞 socket，还有 Python binding 和 libzmq 兼容 C API。内部核心 <code>omq-proto</code> 走的是 sans-I/O ZMTP 3.x 设计，再由 <code>omq-tokio</code> 接入 Tokio。作者还专门强调了热路径上的 <code>yring</code>：一个有 batched flush/prefetch 特性的有界无锁 SPSC ring，用来把批处理收益藏在内部，而不把复杂度甩给 API 使用者。</p>
<p>更妙的是，这个项目甚至把压缩层也顺手工程化了。它支持透明的 <code>LZ4</code> 传输，并且可以从前期消息训练字典，再把字典下发到对端后用于后续流量压缩。再加上 700+ Rust 测试、协议 fuzz、Loom/Miri 验证、以及和 libzmq / zmq.rs / rzmq 的对比基准，<code>OMQ.rs</code> 已经显露出“Rust 消息中间件底座候选”的味道了。</p>
<p>原文链接：https://old.reddit.com/r/rust/comments/1v8y297/omqrs_pure_rust_zeromq_with_libzmqstyle/</p>
]]></description><pubDate>2026-07-29 01:06:35</pubDate></item><item><title>Rust在简单更新接口的优势对比（java,go)</title><link>https://rustcc.cn/article?id=5e150da7-1b99-4837-93df-11136f67691d</link><description><![CDATA[<h3>背景</h3>
<p>在分布式与微服务架构中，API 接口的数据更新（Update）是高频场景。如何优雅地实现“部分字段更新”，准确区分“不修改”、“置空”与“设为默认值”，是衡量工程规范性的关键细节，也直接考验着不同编程语言的类型系统与表达力。</p>
<h4>java实现</h4>
<p>Java 的实现思路非常清晰：利用包装类（Integer、Boolean）的 null 语义来表达“未传”，然后借助 MyBatis 等框架的动态 SQL 能力，只拼接需要更新的字段。</p>
<pre><code>&lt;update id="updateUserSelectiveWithVersion"&gt;
    UPDATE user
    &lt;set&gt;
        &lt;if test="name != null"&gt; name = #{name}, &lt;/if&gt;
        &lt;if test="age != null"&gt; age = #{age}, &lt;/if&gt;
        version = version + 1,
    &lt;/set&gt;
    WHERE id = #{id} AND version = #{version}
&lt;/update&gt;
</code></pre>
<p>优点:</p>
<ol>
<li>基于java生态，无需重复造轮子。</li>
<li>与数据库的null兼容性好。</li>
</ol>
<p>缺点：</p>
<ol>
<li>运行时风险： xml的错误无法在编译阶段发现。</li>
<li>动态sql维护风险。</li>
<li>不修改和置空边界模糊，比如数据库字段设置成"null", 就无法很好区分。</li>
<li>java框架臃肿，反射开销大。</li>
<li>基础类型 int=0, 无法区分是不修改和置空。</li>
</ol>
<h4>go实现</h4>
<p>Go 没有 Java 那样的注解和动态代理机制，它崇尚 "显式优于隐式" 。因此，Go 的实现思路非常直接：用指针或 map[string]interface{} 来表达"字段是否被传入"，然后手动构建更新语句。</p>
<pre><code>func (s *UserService) UpdateUser(req *UpdateUserReq) error {
    // 构建需要更新的字段 map
    updates := make(map[string]interface{})
    
    if req.Name != nil {
        updates["name"] = *req.Name
    }
    if req.Age != nil {
        updates["age"] = *req.Age
    }
    if req.Email != nil {
        updates["email"] = *req.Email
    }
    if req.Active != nil {
        updates["active"] = *req.Active
    }
    
    // 必须有字段需要更新
    if len(updates) == 0 {
        return errors.New("no fields to update")
    }
    
    // 使用 GORM 的 Updates 方法（传入 map）
    result := s.db.Model(&amp;User{}).
        Where("id = ? AND version = ?", req.ID, req.Version).
        Updates(updates)
    
    if result.RowsAffected == 0 {
        return errors.New("update failed: version mismatch or record not found")
    }
    return result.Error
}
</code></pre>
<p>优点：</p>
<ol>
<li>编译期就能确定指针结构体的结构正确，不会出现拼写错误。</li>
<li>通过nil能清晰区分 不修改 和 置空，</li>
<li>无反射，性能较快。</li>
</ol>
<p>缺点：</p>
<ol>
<li>代码重复较多，大量if err != nil.</li>
<li>类型系统表达力有限：Go 没有代数数据类型（ADT），无法像 Rust 的 Option&lt;Option&gt; 那样在类型层面区分"未传"和"传 null"。</li>
<li>会有json类型歧义，不太好区分 {"age": null} 和 {}</li>
<li>Map 方案失去类型安全：使用 map[string]interface{} 虽然灵活，但字段名错误无法在编译期发现，且需要手动类型断言，代码变得脆弱。</li>
</ol>
<h4>rust实现</h4>
<p>Rust 没有 Java 的反射和动态代理，也没有 Go 的 nil 指针。它解决这个问题的思路是在类型层面把三种语义明确区分开(Option&lt;Option&gt;)，让编译器帮你检查所有情况。</p>
<pre><code>use serde::Deserialize;

#[derive(Debug, Deserialize)]
pub struct UpdateUserReq {
    pub id: i64,
    
    // 关键：Option&lt;Option&lt;String&gt;&gt; 
    // 外层 Option 表示"字段是否在 JSON 中出现"
    // 内层 Option 表示"值是否为 null"
    pub name: Option&lt;Option&lt;String&gt;&gt;,
    pub age: Option&lt;Option&lt;i32&gt;&gt;,
    pub email: Option&lt;Option&lt;String&gt;&gt;,
    pub active: Option&lt;Option&lt;bool&gt;&gt;,
}
</code></pre>
<p>优点:</p>
<ol>
<li>编译期安全区分三种类型语意，不修改，修改，置空。</li>
<li>性能快，零运行时开销。</li>
<li>没有歧义: None、Some(None)、Some(Some(T)) 三种状态完全独立。</li>
</ol>
<p>缺点:</p>
<ol>
<li>类型复杂度增加，对于新人来说，理解"双层 Option"的语义需要一定的学习成本。</li>
<li>仍然需要考虑动态sql拼接，代码相对也更冗余，字段多的时候，代码会很长。</li>
<li>sea-orm还在继续完善中。</li>
</ol>
<h4>一句话总结</h4>
<p>Java:生态成熟，框架替你搞定动态SQL，开发效率高。但 null 语义需团队严格约定，否则"不修改"和"置空"易混淆。XML拼写错误和反射开销是运行时才暴露的隐患。
Go:指针加显式判断，控制流清晰透明，性能轻量。但每个字段需手写 if，代码略显啰嗦。更要警惕 encoding/json 无法区分 {} 和 {"age": null} 这个经典陷阱。
Rust:用 Option&lt;Option&gt; 在类型层面彻底区分三种语义，编译器强迫你穷尽所有分支。代码更冗长，双层 Option 有学习成本。但换来的是零开销和编译期绝对安全，Bug无处藏身。</p>
<h4>rust一些常见updator优化点</h4>
<ol>
<li>使用别名或者枚举来优化代码</li>
</ol>
<pre><code>pub type FieldUpdate&lt;T&gt; = Option&lt;Option&lt;T&gt;&gt;;
</code></pre>
<pre><code>#[derive(Debug, Clone, Deserialize)]
#[serde(untagged)]  // 允许 JSON 灵活匹配
pub enum UpdateField&lt;T&gt; {
    Ignore,      // 不修改（对应 JSON 字段缺失）
    SetNull,     // 置空（对应 JSON 传 null）
    Set(T),      // 设为具体值（对应 JSON 传具体值）
}
</code></pre>
<ol start="2">
<li>完善updator的orm专用框架解决 结构体映射到sql的正确性和可维护性。</li>
</ol>
<pre><code>/// 设置方法抽象
pub trait SetMethods&lt;T&gt; {
    /// 设置普通字段（直接赋值，不区分 NULL）
    fn set(&amp;mut self, column: &amp;str, value: T) -&gt; &amp;mut Self;
    
    /// 设置可选字段（支持三种语义）
    fn set_optional(&amp;mut self, column: &amp;str, value: Option&lt;Option&lt;T&gt;&gt;) -&gt; &amp;mut Self;
    
    /// 设置为 NULL
    fn set_null(&amp;mut self, column: &amp;str) -&gt; &amp;mut Self;
}

</code></pre>
]]></description><pubDate>2026-07-28 10:23:53</pubDate></item><item><title>【Rust日报】2026-07-28 Safety in an Unsafe World：Netstack3 用类型系统把“buggy programs don’t compile”推到协议正确性</title><link>https://rustcc.cn/article?id=51f3a6b2-1924-43eb-bc34-b819059d3df2</link><description><![CDATA[<h2>Safety in an Unsafe World：Netstack3 用类型系统把“buggy programs don’t compile”推到协议正确性</h2>
<p>这篇文章来自 RustConf 2024 演讲的整理版，但内容一点都不轻。作者拿 Fuchsia 的 <strong>纯 Rust 网络栈 Netstack3</strong> 当主线，先摆出一个很硬的结果：在近一年的大规模 dogfooding 里，这套实现了大量协议、跨 63 个 crate、约 19.2 万行代码的网络栈，现场环境里只抓出了 3 个 bug；而上线后，相比旧版 Netstack2，崩溃率还直接降了一个量级，内存占用也明显下降。</p>
<p>作者要讲的核心并不是“Rust 天生万能”，而是更进一步：Rust 默认只帮你兜住内存安全和线程安全，但真正复杂系统里的大量 bug——协议状态机、跨模块约束、业务不变量——并不会自动因为你用了 Rust 就消失。Netstack3 的做法，是继续把 <strong>不变量、状态转换、抽象边界</strong> 编进类型和 API 设计里，让外部安全代码根本没机会构造出错误状态，也就是把“buggy programs don’t compile”从语言层扩展到系统设计层。</p>
<p>这种思路对基础设施 Rust 项目尤其有启发。它说明 Rust 的上限不只是“写得更不容易崩”，而是可以把一部分过去只能靠 code review、测试、线上经验慢慢补的正确性约束，前移到编译期去表达。对做协议栈、数据库、编译器、中间件的人来说，这篇文章非常值得反复看。</p>
<p>原文链接：https://joshlf.com/posts/safety-unsafe-world/</p>
<h2>Kata Containers 4.0：默认运行时正式切到 Rust，容器沙箱栈完成平台级换芯</h2>
<p>这次 <code>Kata Containers 4.0</code> 最值得看的，不是零碎功能更新，而是它把那条筹备多年的 <strong>Rust 运行时重写</strong> 正式扶正成默认路径，老的 Go 运行时则进入弃用通道，只会保留到 5.0，后续也不再接收新特性。换句话说，Kata 这次不是“补一版功能”，而是把整个平台底座直接换芯。</p>
<p>从发布说明看，这套默认 Rust 运行时已经覆盖 <code>x86_64</code>、<code>aarch64</code>、<code>s390x</code>，同时继续围绕“容器速度 + 虚拟机隔离”这条路线强化能力：支持 <code>QEMU</code>、<code>Cloud Hypervisor</code>，还把 <strong>Rust 写的 Dragonball hypervisor</strong> 纳入正式支持范围；在设备与资源侧则继续补齐 block device、hotplug、virtio-mem、多队列网络、VFIO coldplug、Kubernetes 资源核算等关键能力。</p>
<p>更重要的是，Kata 4.0 的叙事已经明显从“更安全的容器”扩展到了 <strong>AI / 多租户工作负载的沙箱基础设施</strong>。当容器沙箱、hypervisor、runtime、编排接口这一整层都在往 Rust 上集中，Rust 在云原生安全底座里的存在感又往前拱了一大步。</p>
<p>原文链接：https://opensourcewatch.beehiiv.com/p/kata-containers-4-0-is-less-a-new-feature-release-than-a-platform-reset-to-rust</p>
<h2>Tokio / Smol / Glommio 90 组对比：HTTP 负载下 work-stealing 和 executor-per-thread 到底谁更能打</h2>
<p>这篇基准文章很有料。作者围绕 Rust 生态里几条常见异步运行时路线，搭了一个带真实业务影子的 HTTP/2 测试框架：<code>create_order</code>、<code>index_page</code>、<code>read_order</code> 三类请求，分别模拟 IO 密集、CPU 密集和混合负载，再把 <strong>线程数、流量分布、连接规模、工作负载结构</strong> 组合成 90 组场景，去对比 <code>tokio</code> 的 work-stealing 与多线程共享端口的 executor-per-thread 模式，以及 <code>smol</code>、<code>glommio</code> 这类更偏每线程本地执行器的方案。</p>
<p>文章里最有意思的结论不是“谁永远最快”，而是 <strong>性能高度依赖负载形态</strong>。在流量分配更均匀、CPU 更重的场景里，executor-per-thread 往往能靠本地缓存命中和少同步开销拿到更稳的尾延迟；但在更偏 IO、任务分布更不均衡的情况下，work-stealing 的全局调度又可能更占便宜。作者把“到底该选 Tokio 默认多线程，还是每线程单独跑本地执行器”这个经常被经验主义处理的问题，第一次做成了比较系统的实验。</p>
<p>对 Rust 服务端工程来说，这类文章的价值很直接：它提醒你 <strong>运行时模型本身就是架构选择的一部分</strong>。不是所有 Web 服务都应该默认接受同一套 executor 设计，尤其在高并发、HTTP/2、多核部署、Noisy Neighbor 明显的环境里，调度策略本身就会决定你拿到的是吞吐、尾延迟，还是两边都不上不下。</p>
<p>原文链接：https://c410-f3r.github.io/thoughts/work-stealing-vs-executor-per-thread-evaluating-different-http-server-workloads-with-tokio-smol-and-glommio/</p>
<h2>从零手写 Rust arena：把 UnsafeCell、raw buffer 和类型化句柄一层层拆开</h2>
<p>这篇文章不是泛泛讲“arena allocator 有什么用”，而是真的从最朴素的 <code>Vec&lt;T&gt;</code> 版本开始，逐步把 Rust 里 arena 为什么难、难在哪、最后为什么往往要碰 <code>unsafe</code> 讲清楚了。作者先用数据库、脚本运行时这类高频临时对象场景解释 arena 的现实价值：预分配大块内存、顺序推进指针、整批复用空间，换来更少碎片、更低分配开销、更好的 cache locality。</p>
<p>真正值得看的，是它没有停在“固定容量 Vec”这一层，而是继续往下拆：为什么 <code>Vec&lt;String&gt;</code> 只把 header 放进 arena、底层数据仍然散落在堆上；为什么 borrow checker 不允许你轻松拿到多个指向不同槽位的 <code>&amp;mut</code>；为什么想同时支持 <strong>不同类型、多个可变引用、正确 drop 动态对象</strong>，最后就得自己维护 raw buffer、对齐、cursor、slot 元数据，以及 <code>UnsafeCell</code> 驱动的内部可变性。</p>
<p>这类文章对 Rust 读者很友好的一点，是它没有把 <code>unsafe</code> 写成黑魔法，而是把“什么时候必须自己接管编译器原本替你保证的那部分东西”讲得很具体。对想理解内存池、对象 arena、ECS、数据库执行器甚至自定义分配器的人来说，这是一篇很适合顺着源码边读边想的材料。</p>
<p>原文链接：https://rushter.com/blog/rust-memory-arenas/</p>
]]></description><pubDate>2026-07-28 01:07:07</pubDate></item><item><title>Ideavibes - 使用Rust构建的一个Vibe Shipping平台</title><link>https://rustcc.cn/article?id=06e4924f-8490-4ef7-a200-dfba62b68ba0</link><description><![CDATA[<p>大家都很熟悉 Vibe Coding，但是大家有没有想过，代码写出来之后呢？你有多大比例将这些代码上线为一款真正的产品供用户使用？又有多大比例真正产生了现金流水甚至利润呢？</p>
<p>不要再沉迷于Vibe Coding了。</p>
<p>今天我为大家带来了 Ideavibes.ai 这个 Vibe Shipping 产品。Vibe Shipping 顾名思义就是通过聊天交付产品。它完整包含了 Vibe Coding 的整个过程，并扩展到产品发布的整个生产线。</p>
<p><strong>Ideavibes.ai：The Idea-to-Product Platform</strong><br>
<strong>从想法到产品的一站式交付平台</strong></p>
<p>很多人有好想法，却很难真正把它变成一个可以上线、给用户用的产品。</p>
<p>传统软件开发需要产品经理、设计师、前后端、运维一整支团队，成本高、周期长、门槛高。<br>
现在虽然有了 AI 编程工具，能快速生成代码，但「代码写出来」和「产品真正上线」之间，仍然隔着部署、域名、登录、邮件、支付、监控、扩展等一整套工程链路。</p>
<p>Ideavibes.ai 正是为了解决这个问题而生。</p>
<p>它是 <strong>The Idea-to-Product Platform</strong>——从想法到产品的平台。<br>
你只需要用自然语言描述想法，它的 AI 团队会负责规划、构建、审查、部署和扩展，把对话直接变成可运行的线上产品。</p>
<p>Ideavibes.ai 的核心口号是：</p>
<blockquote>
<p><strong>You talk. We ship.</strong><br>
你说想法，我们交付产品。</p>
</blockquote>
<h3>它主要解决什么问题？</h3>
<ol>
<li>
<p><strong>想法到产品的断层</strong><br>
很多项目卡在「代码写好了，但没法真正上线给用户用」。部署、域名、登录、邮件、支付、监控、自动扩展这些生产环境问题，依然需要专业工程能力。</p>
</li>
<li>
<p><strong>工具碎片化与协作成本高</strong><br>
写代码用一个工具、部署用另一个、配置域名和邮件又得手动处理，中间交接多、容易出错，非技术创始人几乎无法独立完成。</p>
</li>
<li>
<p><strong>雇不起完整技术团队</strong><br>
独立开发者、创作者、一人公司、咨询顾问，往往只有业务洞察和想法，却承担不起完整产品团队的成本。</p>
</li>
<li>
<p><strong>对非技术用户不友好</strong><br>
多数 AI 编程工具仍然要求相对精确的技术指令，或者需要用户自己管理多个 Agent。普通人真正想要的，是「把事情做成」，而不是「协调一支 AI 小队」。</p>
</li>
</ol>
<h3>Ideavibes.ai 怎么做？</h3>
<ul>
<li><strong>多 Agent 协作团队</strong>：不是单个 AI，而是规划、编码、审查、测试、部署等专业化 Agent 一起工作，互相 review。</li>
<li><strong>Intent Engine（意图引擎）</strong>：把你的自然语言想法，自动转成清晰的产品需求、用户故事和可执行任务。</li>
<li><strong>端到端连续交付</strong>：从对话打磨想法 → 生成需求与代码 → 部署到云端 → 绑定域名 → 配置登录/邮件等服务 → 自动扩展，全流程打通，减少工具切换和人工交接。</li>
<li><strong>代码完全归你</strong>：所有代码都在你自己的 GitHub 仓库，透明可审计，没有供应商锁定。</li>
<li><strong>GitHub Issues 作为控制平面</strong>：每次决策、进度和变更都有完整记录，可追踪。</li>
<li><strong>Creator Mode + Expert Mode</strong>：非技术用户用 Creator 模式直接对话推进；有技术背景的用户可用 Expert 模式获得更细控制。</li>
</ul>
<p><strong>底层技术亮点</strong>：整个 infra 流水线与核心服务全部使用 <strong>Rust</strong> 开发。<br>
Rust 带来的内存安全、高性能和可靠性，让平台在自动构建、部署和扩展过程中更稳定、更安全，也更适合真正上线给用户使用的产品。</p>
<p>它支持多种语言和框架，强调高质量与安全交付。</p>
<h3>简单流程</h3>
<ol>
<li>用大白话描述你的产品想法</li>
<li>通过对话打磨问题、目标用户和核心功能</li>
<li>AI 团队生成需求文档、用户故事、代码和可运行应用</li>
<li>部署到云端，查看 Staging 环境，继续迭代</li>
<li>绑定自定义域名，配置生产服务，正式上线</li>
</ol>
<p>Ideavibes.ai 网站上已有多款真实部署的示例（工具类、论坛、习惯追踪、计算器等），证明Vibe Shipping已经不是概念了，已经是可以真实运行的东西了。</p>
<p>Ideavibes.ai 想服务的，不是「会写代码的人」，而是有真实问题、有原创想法、想把想法变成真正产品的人——独立创始人、创作者、产品经理、顾问、教育者等。</p>
<p>软件创作的门槛正在被重新定义。<br>
以前需要一支团队才能完成的事情，现在可以通过一支 AI 工程团队，用对话的方式从 idea 走到 product。</p>
<p>有想法的话，可以直接去 <a href="https://ideavibes.ai" rel="noopener noreferrer">ideavibes.ai</a> 试用。更好的方式是通过我的邀请链接来登录，这样可以直接获取 500 个 credits，用来构建发布一个小项目绰绰有余了：<a href="https://dashboard.ideavibes.ai/?ref=e3fce0e098fd48dbb2b96b48231a4f01&amp;src=user_referral" rel="noopener noreferrer">https://dashboard.ideavibes.ai/?ref=e3fce0e098fd48dbb2b96b48231a4f01&amp;src=user_referral</a></p>
<p>如果有什么疑问，也可以联系我vx：daogangtang。</p>
]]></description><pubDate>2026-07-27 18:19:09</pubDate></item><item><title>【Rust日报】2026-07-27 Stoffel：Rust 把多方安全计算从语言到 QUIC 运行时整条栈全包了</title><link>https://rustcc.cn/article?id=51b167cf-abf5-4803-93a8-7aabdfeed028</link><description><![CDATA[<h2>Stoffel：Rust 把多方安全计算从语言到 QUIC 运行时整条栈全包了</h2>
<p><code>Stoffel</code> 最抓眼球的地方，不是单个 crate，而是它把 <strong>安全多方计算（MPC）</strong> 需要的整条链路几乎都塞进了一个 Rust monorepo：从类似 Cargo 的 <code>stoffel</code> CLI、到 <code>StoffelLang</code> 编译器、Rust SDK、寄存器式 VM，再到真正负责分布式执行的 MPC 后端和网络层，作者想做的是一套“能写、能编、能跑、还能组网”的完整运行时栈。</p>
<p>这个项目的目标也很明确：开发者写看起来比较普通的代码，只把需要保密的值标成 secret，剩下的编译、字节码、执行和参与方协作交给底层去处理。仓库里已经把 <strong>HoneyBadger / AVSS 后端、QUIC 联网、C FFI、typed Rust bindings</strong> 这些本来很容易散落在不同仓库里的东西收拢到一起，还提供了本地执行和真实网络执行两条路径，这让它不像论文 demo，而更像一套认真往产品和平台形态推进的基础设施。</p>
<p>对 Rust 生态来说，<code>Stoffel</code> 的意义在于它把 Rust 擅长的那几件事——类型约束、底层控制、并发与网络工程——直接压到了隐私计算这条高门槛赛道上。过去大家更常看到 Rust 在数据库、代理、编译器和 WebAssembly 上打基础设施，这次则是把 <strong>“可落地的 MPC 工具链”</strong> 作为完整产品形态推出来，想象空间相当大。</p>
<p>原文链接：https://github.com/Stoffel-Labs/stoffel</p>
<h2>vib：终端双栏文件管理器把 LocalSend 文件互传直接做进 TUI</h2>
<p><code>vib</code> 不是那种“能浏览目录就算完事”的 TUI 小工具。作者把它做成了一套更接近日常主力工具的终端文件管理器：<strong>双栏浏览、独立标签、批量选择、文本预览、书签管理</strong> 都已经补齐，而且操作逻辑走的是很典型的键盘工作流路线，明显是冲着长期使用而不是一次性展示去的。</p>
<p>真正让它从“又一个 TUI 文件管理器”里跳出来的，是它把 <strong>LocalSend</strong> 直接接进了终端里。也就是说，你不用先切到图形界面再找传输工具，而是可以在当前终端工作流里直接扫描局域网设备、选中文件、发起传输、接收文件，再继续干活。这个方向挺对味：Rust 近几年很会做 CLI/TUI，但真正把“跨设备互传”这种生活化能力无缝并进终端体验的项目，其实并不多。</p>
<p>从 README 看，<code>vib</code> 已经把很多容易被忽略的细节也处理到了：传输状态提示、确认弹窗、下载落点、重扫网络设备、以及对 Docker / Tailscale 之类多网卡环境的说明，都说明作者不是只做个壳，而是在认真把它往可发布工具上打磨。对喜欢在终端里完成尽可能多事情的人来说，这个组合挺有吸引力。</p>
<p>原文链接：https://github.com/ayanchavand/vib</p>
<h2>push2talk：Whisper 本地语音转写做成跨平台热键输入桌面应用</h2>
<p><code>push2talk</code> 的亮点，不只是“又一个 Whisper 客户端”，而是作者把 <strong>按住热键说话、松开后把转写结果直接打进当前光标位置</strong> 这件事，做成了一套完整的桌面产品，而且坚持 <strong>全程本地运行</strong>：音频不出机器，转写由 <code>whisper-rs</code> / <code>whisper.cpp</code> 完成，Linux 和 macOS 两边都给出了可安装构建。</p>
<p>更有意思的是它暴露出来的那些很 Rust、也很系统工程的实现细节。Linux 侧热键捕获直接读 <code>evdev</code>，绕开 X11 / Wayland；macOS 侧作者没有继续忍 <code>rdev</code> 在真机 Apple Silicon 上的崩溃，而是手写了一套 <strong>CGEventTap</strong>；录音走 <code>cpal</code>，键盘模拟在 Linux 上交给 <code>ydotool</code>、macOS 上交给 <code>enigo</code>，GPU 转写则分别吃 Vulkan 和 Metal。也就是说，这不是简单把几层库缝起来，而是实打实地在桌面平台边界、权限模型和原生输入输出层面做了不少脏活累活。</p>
<p>对 Rust 社区来说，这类项目很能说明 Rust 做桌面工具的现实状态：它不只是“界面能不能画出来”，而是能不能把热键、输入设备、系统权限、GPU 推理和跨平台发布这些容易出坑的地方一并收住。<code>push2talk</code> 这次给出的答案，算是相当硬朗。</p>
<p>原文链接：https://github.com/arunmiriappalli/push2talk</p>
<h2>kibble：轻依赖 Rust 知识摄取与 RAG 工具链把抓取、清洗、检索和 MCP 串成一套</h2>
<p><code>kibble</code> 想做的不是单个 RAG demo，而是一条更长的“知识摄取流水线”：<strong>抓取、爬站、提取、清洗、建库、检索、问答、评估、聚类、HTTP 服务、MCP 工具暴露</strong>，几乎全都放进了一套 Rust CLI / library 里。作者把它定位成一个 <strong>快、轻依赖、可编排</strong> 的知识与数据工具链，把真正吃资源的 OCR、训练、embedding 后端留给外部服务，自己专注在把脏数据收进来、洗干净、变成结构化语料这件事上。</p>
<p>它比较有意思的一点，是没有为了“AI 工具链”这个标签把东西堆得很重。作者专门强调了 <strong>feature-gated library、Tokio 异步、流式 ask API、手写 BM25 + RRF 融合、无 embed 后端时自动降级 lexical-only、<code>reqwest + rustls</code> 避开系统 OpenSSL</strong>，还给了 386 个测试和 MCP 暴露路径。换句话说，它更像是一个 Rust 工程师视角下的 AI 数据基础设施，而不是先摆个聊天壳子再慢慢补底层。</p>
<p>更值得留意的是它把“数据摄取”和“能力摄取”放在了一起：<code>caps</code> 可以安装技能，<code>mcp</code> 可以把整套工具暴露给 agent / harness 使用。这让 <code>kibble</code> 的边界开始从单纯数据管道，往 <strong>Rust 版 agent 工具地基</strong> 靠过去。要是后面这条线继续长，Rust 在 AI 基础设施里的存在感可能会更强。</p>
<p>原文链接：https://github.com/femboyisp/kibble</p>
]]></description><pubDate>2026-07-27 01:08:45</pubDate></item><item><title>【Rust日报】2026-07-26 Lightstream：Rust 数据传输栈公开跑分超过 Arrow Flight</title><link>https://rustcc.cn/article?id=af79535f-96d5-4220-9c01-3f938393a347</link><description><![CDATA[<h2>Lightstream：Rust 数据传输栈公开跑分超过 Arrow Flight</h2>
<p>Lightstream 是一个用 Rust 构建的数据传输栈，目标是把 Apache Arrow、Protobuf 和 MessagePack 这类常见数据格式，以更低门槛接到 TCP、HTTP、QUIC、WebSocket、WebTransport、UDS 和 stdio 等传输层上。项目同时提供 Rust 包和 Python 绑定，底层建立在 Minarrow 之上，强调传输层与数据格式层可以按需组合，而不是把使用者绑死在单一协议里。</p>
<p>这次发布最吸引眼球的是公开 benchmark。作者给出的 50Gbps EC2 网络测试里，Lightstream 在 mixed、numeric、string-heavy 和 wide 四组 workload 上，都跑出了高于 Arrow Flight 的吞吐；在 16 路并发下基本把网卡打到 5.8 GiB/s，p99 batch send time 也控制在接近 p50 的范围内。它同时保留了明确的边界：这是高吞吐数据交换层，不是 Kafka 这类 broker，也不是 Flink 这类流处理引擎。</p>
<p>实现层面，项目用了 zero-copy、64-byte 对齐、arena allocation、mmap，以及可选的 io_uring 等性能手段，定位很明确：把“高性能传数据”这件事抽成一层更容易复用的 Rust 基础设施。</p>
<p>原文链接：https://old.reddit.com/r/rust/comments/1v64qoq/introducing_lightstream_built_in_rust_measured/</p>
<h2>graphplot：Rust 图结构可视化工具，支持 Typst 公式与多布局</h2>
<p>graphplot 面向的是“大图、复杂关系、还得看起来别太老”的那类可视化场景。作者提到，自己在 Rust 里长期处理数据结构时，很难找到一个既能撑住复杂图关系、又能提供现代一点视觉效果的工具，于是干脆做了一个新的 Rust 图绘制方案。</p>
<p>从功能看，这个项目已经不只是把节点和边画出来。它支持在节点和边里直接放 Typst 数学公式，支持多种布局引擎、子图与无限嵌套、自定义明暗主题、路径高亮，以及 SVG、PNG、PDF 导出。对于需要把算法结构、依赖关系或复杂状态流直观展示出来的 Rust 项目，这套组合相当实用。</p>
<p>架构上，graphplot 采用轻量 Rust 客户端加后端 API 的方式来承载布局与 Typst 渲染，原因也很实际：这些渲染引擎本身比较重，需要常驻内存，拆成服务端后更容易把体验和性能都维持住。</p>
<p>原文链接：https://old.reddit.com/r/rust/comments/1v6derg/ive_created_a_graph_plotting_tool_in_rust/</p>
<h2>mimalloc-pprof：把 pprof 堆快照带到 mimalloc</h2>
<p>mimalloc-pprof 这次补上的，是很多长期运行服务都会关心的一块能力：pprof 风格的 heap snapshot profiling。作者的目标很直接，就是把 tcmalloc / jemalloc 常见的堆分析体验带到 mimalloc 上，同时保留 mimalloc 在 Windows、macOS、Linux 多平台可用、易于交叉编译的优势。</p>
<p>除了 profiling，本次实现还顺手处理了 Linux 侧一个更底层的问题：TLS sentinel poisoning。作者给出的说明是，这个 bug 在高压或早期线程初始化场景下可能触发更难排查的崩溃；修掉之后，mimalloc 在“可观测性”和“稳定性”两边都往前推了一步。</p>
<p>如果你在 Rust 里维护长时间运行的服务、守护进程或需要细看堆分配行为的系统，这类面向可移植 allocator 的 profiling 能力，价值会比单纯的 benchmark 数字更直接。</p>
<p>原文链接：https://github.com/zackees/mimalloc-pprof</p>
<h2>rsbtd：基于 libtorrent 的现代 BitTorrent 守护进程</h2>
<p>rsbtd 是一个现代 BitTorrent 守护进程，底层建立在 libtorrent-rasterbar 之上，也就是 qBittorrent、Deluge 同一代引擎系。它把重点放在“守护进程原生化”上：不是只做一个能下载的后台程序，而是围绕远程控制、自动化和现代界面，把整套服务形态补完整。</p>
<p>项目提供 GraphQL API、WebUI，以及面向脚本和日常操作的 rsbtctl CLI。部署路径也比较完整：有 release 安装包、RPM、容器镜像，还支持通过较小的配置面来决定监听地址、token、WebUI 暴露方式和服务端能力。对想把下载服务放进自托管体系的人来说，这种“服务 + API + UI”三件套比单纯桌面客户端更容易接进自动化流程。</p>
<p>从 Rust 生态角度看，rsbtd 的价值不只是“又一个 BitTorrent 工具”，而是把传统上偏 C/C++ 的网络守护进程场景，用 Rust 重新包装成了更现代的可运维服务。</p>
<p>原文链接：https://github.com/namazso/rsbtd</p>
]]></description><pubDate>2026-07-26 01:07:21</pubDate></item><item><title>【Rust日报】2026-07-25 Symbolica 2.2 发布：7000+ 规则把符号积分原生带进 Rust</title><link>https://rustcc.cn/article?id=181b89b7-cfa7-4a41-864c-a2d15f0c7684</link><description><![CDATA[<h2>Symbolica 2.2 发布：7000+ 规则把符号积分原生带进 Rust</h2>
<p><code>Symbolica 2.2</code> 这次升级很硬核：作者把一个高性能符号计算框架继续往前推，核心新东西是<strong>原生、可追踪的符号积分</strong>。它背后不是几条玩具规则，而是把 <strong>7000+ 条 Rubi 积分规则</strong>移植进了 Rust，并用 <strong>72,944 道题目的语料</strong>做了验证。对 Rust 生态来说，这不是常见的“封装一个现有数学库”，而是把计算机代数系统里最难做的一块能力之一，真正往原生实现推进。</p>
<p>更重要的是，这个版本不只给出积分结果，还能返回<strong>逐步应用了哪些规则</strong>。这让它同时具备研究、教学和调试价值：既能拿来算，也能拿来看“它为什么这么算”。文章里还给出了性能数据：完整语料集在 Ryzen 9 5900X 上 8 核跑完约 <strong>18 分钟</strong>，在一个独立测试集上甚至比 Mathematica 里的 Rubi 4.17 还快。</p>
<p>Rust 社区里一直有不少高性能计算、编译器、形式化和科学计算方向的开发者，但真正把符号数学做到这个密度、这个可解释性、还保持原生性能的项目并不多。<code>Symbolica 2.2</code> 让人看到的是：Rust 在“系统语言”这层标签之外，也开始更有底气地碰计算数学基础设施了。</p>
<p>原文链接：https://symbolica.io/posts/symbolic_integration/</p>
<h2>Gaze：用纯 Rust 做 Linux 人脸认证，把 root 侧安全问题正面拿下</h2>
<p><code>Gaze</code> 的亮点不只是“Linux 上的人脸认证”，而是它把这件事放在了一个很适合 Rust 的边界上：<strong>PAM 模块会以 root 权限运行，而且要处理来自摄像头的非可信输入</strong>。作者明确说，之所以整套系统完全用 Rust 写，就是因为这里的内存安全、边界处理和运行时稳定性不是锦上添花，而是产品可靠性的底层要求。</p>
<p>从功能上看，这个项目并不只是做个能亮相的 Demo。它强调一切都<strong>本地运行</strong>，人脸 embedding 不离开机器；支持 <strong>MiniFASNet V2</strong> 的活体检测，避免照片或视频直接骗过系统；如果设备有 IR 摄像头，还能继续增强抗欺骗能力。桌面适配也铺得比较开：GNOME 有原生登录 / 锁屏扩展，Hyprland 有专门的 Hyprlock 支持，KDE Plasma 和其他桌面环境也都考虑到了。</p>
<p>更实际的一点是，作者给出的认证延迟已经做到<strong>稳定低于 900ms</strong>，而且项目已经提供 GTK4 / Adwaita GUI、CLI 以及主流发行版打包。对 Rust 生态来说，<code>Gaze</code> 代表的不是抽象的“Rust 可以写安全软件”，而是 Rust 正在往 Linux 桌面登录、安全认证和 root 侧系统组件这种传统上风险很高的区域稳稳推进。</p>
<p>原文链接：https://old.reddit.com/r/rust/comments/1v5timj/gaze_facial_authentication_for_linux_written_in/</p>
<h2>COSMIC 七个月进化录：纯 Rust 桌面把功能密度继续堆高</h2>
<p>System76 这篇回顾最值得看的，不是单个新特性，而是 <strong>COSMIC</strong> 在首发后七个月里仍然保持着相当密的系统级迭代速度。文章列出来的内容非常杂而且非常“桌面操作系统”——从多全屏窗口工作区、输入协议补全、游戏启动修复，到工作区搜索、文件管理、终端、设置、播放器、截图、门户、外接显示器亮度支持，再到新的系统监视器，几乎每一层都在补。</p>
<p>如果把这些更新拆开来看，会发现 COSMIC 继续在证明一件事：<strong>纯 Rust 做桌面环境</strong> 不只是“能做出几个漂亮应用”，而是真的在往完整桌面栈推进。比如文件管理器拿到了更成熟的多标签、网络路径、Recents 搜索和隐私控制；终端补了快捷键、IME 和标签拖拽；设置则继续把搜索、VPN、主题、键位和无障碍能力往统一体验里收拢。它已经不是一个“未来想法”，而是一个持续交付的工程面产品。</p>
<p>对 Rust 社区来说，COSMIC 的意义一直不只是桌面用户会不会迁移过去，而是它持续提供了一个非常稀缺的样板：当 Rust 不再只写底层组件，而是一路写到 compositor、系统应用和用户交互层时，工程组织和产品完成度到底能走多远。七个月后的答案很明确——它没有停在概念验证，而是在继续变成一套更完整的系统。</p>
<p>原文链接：https://system76.com/blog/post/cosmic-de-first-seven-months</p>
<h2>Rust 编译器跑进浏览器：Weblings 把编译、链接和运行全搬到前端</h2>
<p><code>Weblings</code> 这次最抓眼球的地方，不只是“又一个在线 Playground”，而是它把 <strong>Rust 编译、链接和执行</strong> 这整条链路尽量都搬到了浏览器本地。作者给出的路线很清楚：<code>rustc -&gt; cranelift IR -&gt; waffle IR -&gt; wasm linker</code>，最后产出浏览器可直接运行的 WASM 二进制。也就是说，代码不是发到远端服务器去编，而是在用户自己的浏览器里完成主要工作。</p>
<p>这个方向真正有意思的地方，在于它开始把 Rust 的“可学、可试、可带着走”推到一个新的形态。帖子里提到，小程序的完整编译 / 链接 / 运行时间多数能压到 <strong>100ms 以内</strong>；浏览器里的演示也已经能直接跑带 <code>std</code> 的代码。作者还把 <strong>Rustlings</strong> 整套练习接进了 Web UI，这让它不再只是一个技术演示，而是在往教育、训练和低门槛实验环境上继续扩。</p>
<p>如果这个思路继续成熟，它带来的就不只是“在网页里写 Rust”这么简单。作者明确提到，后面还想探索<strong>在浏览器里完成嵌入式构建和刷写</strong>。这意味着 Weblings 触碰的是 Rust 工具链前端化、本地化和教学场景产品化的一整条线，对 Rust 社区的传播和上手门槛都很有想象空间。</p>
<p>原文链接：https://old.reddit.com/r/rust/comments/1v4pfv6/rust_compilation_in_the_browser_with_wasm/</p>
]]></description><pubDate>2026-07-25 01:05:43</pubDate></item><item><title>Ramag v0.0.1：用 Rust + GPUI 做了一个本地优先的开发者桌面工作台</title><link>https://rustcc.cn/article?id=7f1184a0-2812-4365-a008-4af0c32a5855</link><description><![CDATA[<p>这是一个用 Rust + GPUI 构建的原生开发者桌面工作台，一套应用直接覆盖三条高频工作流：</p>
<pre><code>查数据库 ↔ 管 Git 工作区 ↔ 找回并粘贴上下文
</code></pre>
<p>它不是概念 Demo，也不是只搭好了界面的空壳。MySQL、PostgreSQL、Redis、MongoDB 的查询与迁移，Git 的 Diff、提交与分支操作，以及带本地加密的剪贴板历史都已经可以实际使用。</p>
<ul>
<li>GitHub：https://github.com/tools-rs/ramag</li>
<li>Release：https://github.com/tools-rs/ramag/releases</li>
<li>架构说明：https://github.com/tools-rs/ramag/blob/main/docs/architecture.md</li>
<li>性能报告：https://github.com/tools-rs/ramag/blob/main/docs/performance.md</li>
</ul>
<p>现在可以直接下载 macOS Apple Silicon、macOS Intel 和 Windows x64 安装包。</p>
<p>如果你每天都在数据库客户端、Git 工具、编辑器和剪贴板工具之间反复切换，Ramag 就是为这个问题做的。</p>
<p>它不是把三个按钮塞进同一个窗口，而是让三个工具共享统一的窗口、标签、快捷键、主题和本地数据边界：</p>
<pre><code>数据库工作台 + Git 工作台 + 剪贴板工作台
</code></pre>
<p><img src="https://raw.githubusercontent.com/tools-rs/ramag/main/docs/screenshots/home-dark-clipboard-enabled.png" alt="Ramag 首页"></p>
<p>Ramag 不依赖浏览器壳，不要求登录账号，也不会把数据库连接、Git 仓库或剪贴板内容上传到 Ramag 服务。密码和剪贴历史加密后保存在本地，主密钥进入系统凭据库。</p>
<h2>一、它已经能做什么</h2>
<h3>1. 数据库工作台</h3>
<p>当前支持四类数据库：</p>
<ul>
<li>MySQL</li>
<li>PostgreSQL</li>
<li>Redis</li>
<li>MongoDB</li>
</ul>
<p>连接管理、查询、结果浏览、数据编辑和导入导出都在同一个工作台中完成。</p>
<p><img src="https://raw.githubusercontent.com/tools-rs/ramag/main/docs/screenshots/database-connections-light.png" alt="四类数据库统一连接管理"></p>
<h4>MySQL 与 PostgreSQL</h4>
<p>SQL 工作流已经覆盖：</p>
<ul>
<li>Schema、表、视图、列、索引和 DDL 浏览</li>
<li>SQL 高亮、补全和格式化</li>
<li>多语句执行和执行光标所在 SQL</li>
<li>EXPLAIN 与查询取消</li>
<li>结果分页、排序和筛选</li>
<li>单元格编辑与查询历史</li>
<li>表级 JSONL 导入导出</li>
<li>Schema 或数据库级 SQL 导入导出</li>
</ul>
<p>大整数、高精度数值、JSON/JSONB、二进制、日期时间以及 PostgreSQL 原生类型都做了保真处理，避免在 UI 展示和导出过程中丢失精度。</p>
<p>大表读取使用分页和资源预算，不会一次性把整张表加载进内存。带主键的表在导出时优先使用 keyset 分页，避免深分页反复扫描前面的数据。</p>
<p><img src="https://raw.githubusercontent.com/tools-rs/ramag/main/docs/screenshots/database-mysql-query-dark.png" alt="MySQL 查询与十万行分页"></p>
<h4>Redis</h4>
<p>Redis 工作流覆盖：</p>
<ul>
<li>使用 <code>:</code> 自动折叠 Key 命名空间</li>
<li>SCAN 游标遍历和大型 Keyspace 虚拟列表</li>
<li>String、Hash、List、Set、ZSet、Stream</li>
<li>TTL 查看和修改</li>
<li>大 String 有界加载和大集合分批加载</li>
<li>Key 新增、编辑和删除</li>
<li>内置 Redis 命令控制台</li>
<li>整库 JSONL 导入导出</li>
</ul>
<p>导入导出会保留 Redis 数据类型、TTL、List 顺序、ZSet 分数、Stream ID 和二进制内容。命令控制台会对危险、阻塞和生产环境写命令进行识别，减少误操作风险。</p>
<h4>MongoDB</h4>
<p>MongoDB 工作流覆盖：</p>
<ul>
<li>Database、Collection、索引和统计信息浏览</li>
<li><code>find</code>、<code>aggregate</code> 与通用命令</li>
<li>多查询标签、查询历史和 JSON 格式化</li>
<li>文档表格化展示和编辑</li>
<li>Collection 级 JSONL 导入导出</li>
<li>Database 级导入导出</li>
</ul>
<p>嵌套对象会按 dotted path 展开。ObjectId、Decimal128、DateTime、Int64 等 BSON 类型使用 Extended JSON 往返，避免转换成普通 JSON 后丢失类型信息。</p>
<h3>2. Git 工作台</h3>
<p>Git 工作台仍标记为试验性功能，但日常核心工作流已经打通：</p>
<pre><code>打开仓库 → 查看工作区 → 检查 Diff → Stage → Commit → Push / Pull
</code></pre>
<p>当前支持：</p>
<ul>
<li>Changes、Project Files 和 Stash</li>
<li>提交历史、Commit 详情、Blame 和 Reflog</li>
<li>Unified Diff 与 Split Diff</li>
<li>Stage、Unstage 和 Amend</li>
<li>Branch、Tag、Merge、Rebase 和 Cherry-pick</li>
<li>冲突处理</li>
<li>文件编辑与自动保存</li>
</ul>
<p>Diff 和文件内容支持多种语言的语法高亮，大型 Diff 使用虚拟化展示。文件监听按变化路径增量刷新，普通文件保存不会触发整个仓库的完整扫描。</p>
<p><img src="https://raw.githubusercontent.com/tools-rs/ramag/main/docs/screenshots/git-workspace-dark.png" alt="Git 工作区、文件编辑与提交历史"></p>
<p>Git 实现上没有完全重新实现一套凭据和网络认证体系。Ramag 使用 <code>gix</code> 发现仓库，同时让写操作和网络操作复用系统 Git、SSH Agent、Git 配置及已有凭据链。</p>
<p>这保证了 Ramag 与用户现有 Git 环境一致，不会在应用里再造一套和命令行行为不同的认证系统。</p>
<h3>3. 剪贴板工作台</h3>
<p>剪贴板模块支持：</p>
<ul>
<li>纯文本、富文本、链接、颜色、图片和文件路径</li>
<li>来源应用记录</li>
<li>类型筛选与关键词搜索</li>
<li>纯文本复制</li>
<li>自动切回来源应用并粘贴</li>
<li>按数量和保存时间自动清理</li>
</ul>
<p>全局快捷键：</p>
<pre><code>macOS：⌘⇧V
Windows：Ctrl+Shift+V
</code></pre>
<p>打开抽屉后，可以搜索历史记录，通过键盘选择并粘贴回原来的应用。剪贴板采集默认关闭，需要用户在设置中主动启用。</p>
<p>macOS 下会识别 Concealed 和 Transient 等剪贴板隐私标记。还可以配置来源应用黑名单，避免采集密码管理器等敏感应用的内容。</p>
<p>Windows 关闭主窗口后，Ramag 可以驻留系统托盘，剪贴板采集和全局快捷键仍可继续工作。</p>
<p><img src="https://raw.githubusercontent.com/tools-rs/ramag/main/docs/screenshots/clipboard-settings-light.png" alt="剪贴板隐私与采集设置"></p>
<h2>二、为什么坚持 Rust + GPUI 原生实现</h2>
<p>Ramag 从第一天就确定做原生桌面应用，不使用 WebView 或 Electron。</p>
<p>主要技术栈包括：</p>
<ul>
<li>Rust 2024</li>
<li>GPUI 与 gpui-component</li>
<li>sqlx</li>
<li>redis-rs</li>
<li>MongoDB 官方 Rust Driver</li>
<li>gix</li>
<li>redb</li>
<li>aes-gcm</li>
<li>tokio 与 smol</li>
</ul>
<p>选择 Rust 不是为了技术标签，而是因为这个产品天然需要 Rust 擅长的能力。</p>
<p>数据库结果、Git Diff、剪贴板图片都很容易碰到大数据量，内存边界、并发模型和资源生命周期必须明确。</p>
<p>应用还要同时接入数据库、Git、系统剪贴板、系统凭据库、文件监听和原生桌面窗口，这正是 Rust 适合的系统集成场景。</p>
<p>更重要的是，耗时操作和 UI 线程的边界从架构阶段就必须清楚，而不是界面卡顿后再到处补异步任务。</p>
<p>GPUI 的优势是原生渲染和 Rust 内部一致的状态管理模型。不过它目前仍在快速演进，依赖通常需要直接跟随 Git 版本，编译时间和 API 稳定性也是实际开发中必须面对的问题。</p>
<h2>三、不是“能跑就行”：18 个 crate 的清晰边界</h2>
<p>Ramag 是一个由 18 个 crate 组成的 Cargo workspace，采用务实版本的 Clean Architecture。</p>
<p>整体依赖关系如下：</p>
<pre><code>ramag-bin
├── ramag-tool-*       功能界面
├── ramag-ui           GPUI 主壳与共享组件
├── ramag-infra-*      数据库、Git、剪贴板、隧道和存储适配器
├── ramag-app          用例编排
└── ramag-domain       实体与抽象接口
</code></pre>
<p>依赖方向只能向内。</p>
<h3><code>ramag-domain</code></h3>
<p>领域层只定义实体和抽象接口，不依赖 GPUI、sqlx、Redis、MongoDB 或 redb。</p>
<p>不同数据模型使用不同接口：</p>
<ul>
<li>SQL 使用 <code>Driver</code></li>
<li>Redis 使用 <code>KvDriver</code></li>
<li>MongoDB 使用 <code>DocDriver</code></li>
<li>Git 使用 <code>GitDriver</code></li>
<li>本地持久化使用 <code>Storage</code></li>
</ul>
<p>没有为了“统一”而把所有后端都塞进同一个通用 Driver。SQL、KV、文档数据库和 Git 的方法集合差别很大，强行统一通常会产生大量没有实际语义的 <code>NotImplemented</code>，最终反而让接口更难理解。</p>
<h3><code>ramag-app</code></h3>
<p>应用层负责连接管理、数据库操作、导入导出、剪贴板采集和工具注册等业务用例编排。它只依赖领域接口，不知道具体数据库驱动和 GUI 实现。</p>
<h3><code>ramag-infra-*</code></h3>
<p>基础设施层提供 MySQL、PostgreSQL、Redis、MongoDB、Git、剪贴板、SSH 隧道和 redb 本地存储的具体实现。</p>
<p>MySQL 和 PostgreSQL 之间还有一个 <code>ramag-infra-sql-shared</code>，集中处理 tokio runtime、连接池缓存、SQL 多语句切分、LIMIT 注入、错误映射和 Driver 模板实现，减少两个 SQL Driver 之间的重复代码。</p>
<h3><code>ramag-tool-*</code> 与 <code>ramag-bin</code></h3>
<p>Tool 层承载数据库、Redis、MongoDB、Git 和剪贴板界面。最终由 <code>ramag-bin</code> 完成依赖注入、工具注册、快捷键绑定和平台生命周期管理。</p>
<h2>四、GPUI、tokio 和 smol 如何协作</h2>
<p>GPUI 内部使用 smol，而 sqlx、redis-rs 和 MongoDB Driver 依赖 tokio runtime，这是应用必须正面解决的运行时边界。</p>
<p>直接在 GPUI 执行环境中调用这些驱动会因为缺少 tokio reactor 出现运行时问题。Ramag 为不同负载维护独立 runtime：</p>
<table>
<thead>
<tr>
<th>Runtime</th>
<th>用途</th>
</tr>
</thead>
<tbody>
<tr>
<td>smol</td>
<td>GPUI 事件循环</td>
</tr>
<tr>
<td>tokio SQL runtime</td>
<td>MySQL 与 PostgreSQL</td>
</tr>
<tr>
<td>tokio Redis runtime</td>
<td>Redis</td>
</tr>
<tr>
<td>tokio MongoDB runtime</td>
<td>MongoDB</td>
</tr>
<tr>
<td>有界线程池</td>
<td>redb 与系统 Git 等同步操作</td>
</tr>
</tbody>
</table>
<p>没有把所有数据库操作全部塞进同一个 tokio runtime。主要原因是 Redis 订阅、数据库长查询等任务的生命周期和负载差别较大，独立 runtime 可以减少某一类慢任务挤占其它数据库任务线程的情况。</p>
<p>同步 API 则通过固定上限的 worker pool 桥接到异步接口，避免高频操作不断创建新线程。</p>
<h2>五、本地存储和数据安全</h2>
<p>Ramag 使用 redb 保存数据库连接、查询历史、Git 仓库列表、用户偏好和剪贴板历史。</p>
<p>当前设计包括：</p>
<ul>
<li>使用 AES-256-GCM 加密连接密码和敏感配置</li>
<li>主密钥存入 macOS Keychain 或 Windows Credential Manager</li>
<li>剪贴板正文、来源信息、原图和缩略图加密保存</li>
<li>导出文件通过临时文件完整写入后再原子替换</li>
<li>外部路径、连接标识和导入内容进入执行层前进行校验</li>
<li>日志文件设置大小上限并进行滚动</li>
</ul>
<p>应用数据保存在操作系统标准用户数据目录中，核心数据库文件为 <code>ramag.redb</code>。卸载 Ramag 不会自动删除用户数据库、凭据、剪贴板媒体和日志，避免卸载或覆盖升级时误删用户数据。</p>
<h2>六、性能方面做了什么</h2>
<p>这个项目没有把“Rust 写的”直接等同于“自然就快”。</p>
<p>Ramag 使用这些策略控制资源消耗：</p>
<ul>
<li>增量刷新代替全量刷新</li>
<li>分页读取代替一次性载入</li>
<li>keyset 分页代替深 OFFSET</li>
<li>虚拟列表代替一次构造所有行</li>
<li>大 String 和大集合分批加载</li>
<li>查询结果设置行数与字节预算</li>
<li>图片生成缩略图并限制并发加载数量</li>
<li>CPU 和 IO 操作移出 UI 线程</li>
<li>剪贴板最近记录使用有界缓存</li>
<li>Git 文件变化尽量按路径刷新</li>
</ul>
<p>在 Apple M1 Max、Release 构建和本地 Docker 数据库环境中，部分测试结果如下：</p>
<table>
<thead>
<tr>
<th>场景</th>
<th align="right">结果</th>
</tr>
</thead>
<tbody>
<tr>
<td>当前仓库完整 VCS 刷新</td>
<td align="right">16.217 ms 中位数</td>
</tr>
<tr>
<td>当前仓库单路径状态</td>
<td align="right">11.779 ms 中位数</td>
</tr>
<tr>
<td>100,000 条 VCS 状态补丁合并</td>
<td align="right">91.917 μs</td>
</tr>
<tr>
<td>100,000 次提交图布局</td>
<td align="right">2.553 ms</td>
</tr>
<tr>
<td>MySQL 100,005 行导出</td>
<td align="right">871 ms</td>
</tr>
<tr>
<td>PostgreSQL 100,004 行导出</td>
<td align="right">884 ms</td>
</tr>
<tr>
<td>MongoDB 125,102 文档导出 / 导入</td>
<td align="right">1.761 s / 2.371 s</td>
</tr>
<tr>
<td>Redis 45,014 Key 导出 / 导入复核</td>
<td align="right">1.569 s / 13.794 s</td>
</tr>
<tr>
<td>读取 100 万条剪贴历史中的最近 500 条</td>
<td align="right">5.513 ms 中位数</td>
</tr>
<tr>
<td>100 万条剪贴历史完全无命中搜索</td>
<td align="right">219.915 ms 中位数</td>
</tr>
<tr>
<td>4K 图片生成缩略图</td>
<td align="right">58.603 ms，后台执行</td>
</tr>
</tbody>
</table>
<p>这些数据不是跨设备的通用性能承诺，主要用于记录测试环境、发现退化，以及验证资源边界是否真的有效。完整测试方法、P95、数据库种子规模和优化对照可以查看项目中的性能报告。</p>
<h2>七、如何运行</h2>
<h3>直接下载安装</h3>
<p>可以从 GitHub Releases 下载：</p>
<p>https://github.com/tools-rs/ramag/releases</p>
<p>当前提供：</p>
<pre><code>Ramag-&lt;version&gt;-windows-x64-setup.exe
Ramag-&lt;version&gt;-macos-arm64.dmg
Ramag-&lt;version&gt;-macos-x86_64.dmg
SHA256SUMS.txt
</code></pre>
<p>支持范围：</p>
<ul>
<li>macOS 12+ Apple Silicon</li>
<li>macOS 12+ Intel</li>
<li>Windows 10/11 x64</li>
</ul>
<p>当前暂不支持 Linux。Git 功能需要系统 Git，SSH 隧道需要系统 OpenSSH。</p>
<h3>从源码运行</h3>
<p>项目通过 <code>rust-toolchain.toml</code> 固定了 Rust nightly。macOS 需要先安装 Xcode Command Line Tools：</p>
<pre><code>xcode-select --install
</code></pre>
<p>然后运行：</p>
<pre><code>git clone https://github.com/tools-rs/ramag.git
cd ramag
make develop
</code></pre>
<p>Windows 需要 Visual Studio C++ Build Tools 和 Windows 10/11 SDK，然后可以运行：</p>
<pre><code>cargo run -p ramag-bin
</code></pre>
<p>常用质量检查命令：</p>
<pre><code>make fmt-check
make check
make clippy
make test
</code></pre>
<p>如果本机有 Docker，还可以启动 MySQL、PostgreSQL、Redis 和 MongoDB 的完整集成测试环境：</p>
<pre><code>make db-test
</code></pre>
<h2>八、现在就能用，但边界必须说清</h2>
<p><code>v0.0.1</code> 是第一个公开版本，核心工作流已经可用，但下面这些边界必须明确：</p>
<ul>
<li>Git 工作台仍属于试验性功能</li>
<li>Windows 安装包尚未做 Authenticode 签名</li>
<li>macOS 应用尚未做 Developer ID 签名和 Apple 公证</li>
<li>Windows 可能显示未知发布者或 SmartScreen 提示</li>
<li>macOS 下载后可能被 Gatekeeper 阻止</li>
<li>Windows on ARM 尚未完成正式人工验收</li>
<li>当前不支持 Linux</li>
<li>GPUI 仍在快速演进，升级可能带来接口兼容问题</li>
<li>首次源码编译需要下载并编译较多依赖，耗时较长</li>
</ul>
<p>这不会影响你下载、连接本地测试库和体验完整工作流。涉及生产数据库或关键 Git 写操作时，请像使用任何早期开发工具一样确认目标环境并保留恢复点。</p>
<h2>九、下载、Star，也欢迎直接挑战它</h2>
<p>Ramag 已经把第一版完整交出来了。现在最需要的不是客气的鼓励，而是真实使用和具体问题：</p>
<ul>
<li>Rust 原生桌面应用的交互和性能体验</li>
<li>GPUI 在独立桌面工具中的实际使用体验</li>
<li>Cargo workspace 和分层方式是否合理</li>
<li>多种异步 runtime 的隔离方式是否还有更合适的方案</li>
<li>数据库结果表格和大型列表的渲染体验</li>
<li>Git 工作台还缺少哪些关键工作流</li>
<li>Windows 和 macOS 上的兼容性问题</li>
<li>安装、首次启动和编译过程中遇到的问题</li>
</ul>
<p>如果它解决了你的问题，请给项目一个 Star；如果它哪里做得不够好，请直接提 Issue。附上操作系统、Ramag 版本、复现步骤和必要日志，我会按可复现问题继续推进。提交日志前请删除数据库连接、用户名、密码和业务数据。</p>
<h2>十、项目链接</h2>
<ul>
<li>GitHub：https://github.com/tools-rs/ramag</li>
<li>Releases：https://github.com/tools-rs/ramag/releases</li>
<li>Issues：https://github.com/tools-rs/ramag/issues</li>
<li>架构说明：https://github.com/tools-rs/ramag/blob/main/docs/architecture.md</li>
<li>性能报告：https://github.com/tools-rs/ramag/blob/main/docs/performance.md</li>
<li>构建与发布：https://github.com/tools-rs/ramag/blob/main/docs/desktop-release.md</li>
<li>License：Apache-2.0</li>
</ul>
<p><strong>数据库、Git、剪贴板，不需要再开三个工具。</strong></p>
<p>现在就可以下载 Ramag v0.0.1：</p>
<p>https://github.com/tools-rs/ramag/releases</p>
<p>如果你关心 Rust 原生桌面应用、GPUI、数据库工具或 Git 可视化，欢迎 Star、试用、提 Issue，也欢迎直接参与开发。</p>
]]></description><pubDate>2026-07-24 09:58:27</pubDate></item><item><title>Looking for Execution Systems Dev</title><link>https://rustcc.cn/article?id=384d6f0a-8cb7-4904-b4ac-eb601ff874c0</link><description><![CDATA[<p>Requirements</p>
<ol>
<li>Programming &amp; Systems</li>
</ol>
<ul>
<li>熟悉 Rust ，精通 Python</li>
<li>对编译原理、Data Warehouse 有自己的理解</li>
</ul>
<ol start="2">
<li>Trading Pipeline Tooling
专注交易链路上的工具开发，包括：</li>
</ol>
<ul>
<li>data pipeline 、message bus 扩展</li>
<li>risk control tools</li>
<li>execution algo</li>
</ul>
<ol start="3">
<li>Framework</li>
</ol>
<ul>
<li>了解 Nautilus Trader 框架 https://github.com/nautechsystems/nautilus_trader</li>
</ul>
<p>Compensation</p>
<ul>
<li>Base Salary: $5,000 – $10,000 USD / month</li>
<li>Bonus: Project based bonus</li>
<li>Office Location: Hong Kong / Remote</li>
</ul>
<p>Contact Email: profilesai@proton.me
(请直接提交简历和个人 GitHub 数学，统计学专业优先考虑)</p>
]]></description><pubDate>2026-07-24 03:55:33</pubDate></item><item><title>【Rust日报】2026-07-24 Arctic 发布：无锁并发有序映射进了 Rust 生态</title><link>https://rustcc.cn/article?id=fe93c248-d1d7-449d-b4f1-d59222380c77</link><description><![CDATA[<h2>Arctic 发布：无锁并发有序映射进了 Rust 生态</h2>
<p><code>arctic</code> 这次值得关注，不只是因为它又给 Rust 生态加了一个并发容器，而是因为作者瞄准的是更难啃的一类基础设施：<strong>并发、有序、还要尽量无锁</strong>。项目来自 OSDI '26 论文背景，底层基于 <strong>adaptive radix tree (ART)</strong>，目标场景非常直接——像 LSM memtable、MVCC 数据库索引这类既要有序访问、又要高并发读写的数据结构。</p>
<p>作者给出的能力边界很硬：<strong>lock-free 的线性一致写入</strong>、<strong>wait-free 的线性一致读取</strong>，以及 <strong>wait-free 的 prefix / range scan</strong>。这不是那种只在 README 里写几句“高性能”就结束的发布，帖子里还直接拿 80 个物理核、95% 读 5% 写的 workload 去和一批学术界 C/C++ 系统以及 Rust crate 做横向比较，想表达的是：Rust 在这类高并发有序索引上，不一定只能当“安全但慢一点”的实现。</p>
<p>更重要的是，这个项目把很多 Rust 开发者平时只会在论文里看到的主题拉近了一步：内存序、指针 provenance、SIMD、测试，以及类型安全和编译复杂度之间的现实权衡。对做数据库、KV、存储引擎和系统并发结构的人来说，<code>arctic</code> 不是普通的新 crate，而更像是一个能拿来研究和试用的高阶样板。</p>
<p>原文链接：https://old.reddit.com/r/rust/comments/1v4f05y/announcing_arctic_a_lockfree_concurrent_ordered/</p>
<h2>oximo v0.5.0 发布：宏式建模、自动微分与锥优化一起补强</h2>
<p>做数学优化建模的人，往往一眼就能看出 <code>oximo</code> 0.5.0 这次升级的价值所在：它不只是加几个 solver backend，而是开始把“Rust 里写优化模型”这件事做得更像一门真正可用的 DSL。新版本最显眼的是 <strong>macro-based modeling API</strong>，思路明显借鉴了 JuMP 和 <code>good_lp</code> 这类成熟建模体验，让变量、约束、目标函数的表达更贴近建模语言，而不是手工在宿主语言里拼很多样板代码。</p>
<p>除此之外，0.5.0 还把能力边界实打实往前推了一截：<strong>自动微分</strong> 走上了 <code>std::autodiff</code> / Enzyme 路线（目前 nightly），solver backend 新增 <strong>Clarabel</strong> 和 <strong>Pounce</strong>，模型类型进一步扩到 <strong>SOCP</strong> 与 <strong>MISOCP</strong>。这意味着它已经不再只是“小而美的 LP/QP 玩具库”，而是在朝更完整的代数优化建模工具链发展。</p>
<p>对 Rust 生态来说，优化建模一直不是最热闹的赛道，但也正因为如此，像 <code>oximo</code> 这种把 DSL 可读性、自动微分和求解器接入一起往前推的项目，反而更值得留意。它很可能不会像通用 Web 框架那样瞬间刷屏，但对科研、运筹、工业优化和教学场景来说，这类库一旦成熟，黏性会非常强。</p>
<p>原文链接：https://old.reddit.com/r/rust/comments/1v4av6j/oximo_v050/</p>
<h2>Battery packs：把“该选哪些 crate”变成可发布、可演化的默认组合</h2>
<p>Niko Matsakis 这篇关于 <strong>battery packs</strong> 的文章，戳中的其实是 Rust 新用户和团队迁移者最常见的一个痛点：生态太丰富当然是好事，但每次从零比较 CLI、Web、嵌入式、错误处理、CI 配套 crate，也确实很消耗决策精力。battery pack 的想法，就是把一组围绕共同主题整理好的 crate 推荐集打包成一个可发布、可演化的“默认组合”，让大家先有一条靠谱起跑线，而不是每次都从 crates.io 的海里重新捞。</p>
<p>这个设计有意思的地方在于，它<strong>不是新的标准库，也不是强绑定的框架</strong>。battery pack 本身可以作为 crate 发布，里面的依赖、feature 和模板就代表推荐组合；用户最终依赖的依旧是里面的具体 crate，而不是对 battery pack 建一个硬耦合。这意味着它既能给新手和团队提供“先这样选通常没错”的默认答案，又不会把生态冻结成唯一正解。今天你跟着 pack 起步，明天发现更适合自己的替代品，完全可以换。</p>
<p>文章还把这件事往组织层面再推了一层：工作组、商业网络、垂直领域社区都可以发布自己的 battery pack，把“我们真正在生产里用什么”公开成一套共享建议。对 Rust 来说，这比单纯再多几篇“生态推荐清单”更进一步，因为它开始尝试把经验、默认实践和互操作方向，变成一套可分发、可更新、甚至能带模板和自动化动作的生态机制。</p>
<p>原文链接：https://smallcultfollowing.com/babysteps/blog/2026/07/15/battery-packs/</p>
<h2>coral-rs：纯 Rust 驱动把 Google Coral USB Accelerator 救活</h2>
<p><code>coral-rs</code> 的亮点很鲜明：作者没有去继续包一层早就摇摇欲坠的 C++ 旧栈，而是直接把 <strong>Google Coral USB Accelerator</strong> 的 userspace 协议在 Rust 里重新跑通。项目建立在 <code>nusb</code> 之上，整个推理路径做到<strong>进程里没有 C 依赖</strong>，从 DFU 刷固件、vendor control transfer 拉起设备，到从 <code>*_edgetpu.tflite</code> 里拆出模型、补设备地址、再经 bulk endpoint 把指令流送进去，整条链路都用纯 Rust 实现。</p>
<p>更妙的是，这不是“能跑起来就算成功”的移植。作者拿真实硬件验证后，给出的结果是：在同一设备和同一图片上，输出和官方栈 <strong>bit-identical</strong>，但性能居然还更快——帖子里提到 <code>mobilenet v1</code> 走 USB3 时，单次推理 <strong>6.8ms vs 12.1ms</strong>。也就是说，它不仅把一个被官方半放弃、生态逐渐发霉的设备重新带回现代环境，还顺手证明了 Rust 直接写这类硬件侧运行时并不只是“可行”，而是有机会做得更轻、更快。</p>
<p>当然限制也讲得很实在：当前只支持 <strong>USB Accelerator</strong>，M.2 / PCIe 不是同一条路径；模型编译仍然离不开 Google 的闭源编译器。但即便如此，对关心边缘推理、USB 设备栈、推理 runtime 或纯 Rust 硬件接入的人来说，<code>coral-rs</code> 这类项目的示范意义已经很强了。</p>
<p>原文链接：https://old.reddit.com/r/rust/comments/1v48nwq/coralrs_a_pure_rust_driver_for_the_google_coral/</p>
]]></description><pubDate>2026-07-24 01:07:08</pubDate></item><item><title>GitBundle v3.5</title><link>https://rustcc.cn/article?id=4f9a98e9-2ea8-4029-b455-1da1e040702a</link><description><![CDATA[<p>大家好, 我是一名独立开发者, 同时也是 <a href="https://github.com/gitbundle/gitbundle" rel="noopener noreferrer">GitBundle</a> 的项目作者, 在这个项目上持续投入了巨量的时间和精力, 经过持续的迭代和打磨，GitBundle 终于迎来了 v3.5 版本。这次更新在安全性、CI 交互和用户体验上都做了重点提升，希望给大家带来更好的自托管 Git 体验。</p>
<p>🔐 安全性大幅增强</p>
<ul>
<li>移除 SHA-1，新增 SSH layer，支持后量子密钥交换算法 mlkem768x25519，彻底修复 SSH 安全警告</li>
<li>修复了 git clone 无法安全断开 TCP 连接的问题</li>
</ul>
<p>⚙️ 后台管理优化</p>
<ul>
<li>支持用户软删除，数据管理更灵活</li>
<li>支持用户安全更新邮箱</li>
</ul>
<p>🚀 CI 与体验提升</p>
<ul>
<li>支持 cursor-based CI 日志拉取，交互更顺畅</li>
<li>UI 全面打磨，修复了多项历史遗留问题，视觉和操作更一致流畅</li>
</ul>
<p>为什么要做这个项目:
第一点: 肯定是因为兴趣爱好, 因为我喜欢写代码, 喜欢做这个事情
第二点: 我见识过类似的各种平台, 但都是差强人意, 体验很糟糕
第三点: 的的确确我找不到工作, 失业了, 职场远远不是你想写代码那么简单, 这是一个很痛的现实, 但我必须要接受, 因为我还想继续写代码直到写不动的那一天, 可现实不允许我这样</p>
<p>欢迎大家下载试用，也期待大家的反馈和建议！ 🙏</p>
<p>关于大家关心的源代码开源问题, 目前有计划在将来进行开源, 但具体开源时间还不确定.</p>
<p>详细发布日志:</p>
<p>https://github.com/gitbundle/gitbundle/releases/tag/server-v3.5.0</p>
]]></description><pubDate>2026-06-11 11:48:16</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></channel></rss>