C/C++ 和 Rust 的战争,最近几年在技术社区就没停过。有人觉得 Rust 是来革 C++ 命的,也有人觉得它只是又一个炒作周期。站在项目开发的角度,这两类语言的差异根本不在语法层面,而在工程实践的每一个角落:内存怎么管、模块怎么组织、并发怎么保证、错误怎么传播、构建怎么跑、团队怎么协作。我在嵌入式网关、网络中间件和桌面工具上都用过这两种语言,今天这篇就从真实项目的视角,把 C/C++ 项目与 Rust 项目的区别掰开揉碎聊一遍,帮你判断下一个项目到底该选谁。
很多人在网上问"Rust 是不是比 C++ 难""要不要把 C++ 项目重写成 Rust",这类问题本身就是伪命题。语言选型不是比谁更酷,而是比谁在你的业务约束里更合适。C/C++ 有几十年的积累,生态覆盖硬件驱动、游戏引擎、操作系统底层;Rust 则在内存安全、并发安全和工程化体验上做了完全的重新设计。这两者各有不可替代的领域,但如果你正在从零开始一个新项目,特别是那种要长期维护、并发密集、安全要求高的项目,Rust 的胜率会越来越大。
1. 从项目选型看起:C/C++ 与 Rust 的"根"就不一样
1.1 都在谈"系统编程",其实目标完全不同
C 语言诞生于 1972 年,设计目标就一句话:用最少的运行时开销操作硬件。C++ 在 80 年代作为 C 的超集出现,加上了类、继承、模板、异常这些抽象机制,本质上是在"性能"和"抽象能力"之间找平衡。Rust 则来自 2010 年代,Mozilla 在研发 Servo 浏览器引擎时发现,用 C++ 写高并发浏览器组件,内存安全漏洞防不胜防,于是决定发明一种语言:既要 C++ 级别的性能和控制力,又要通过编译器在编译期就把内存错误、数据竞争挡在门外。
这个"出身"决定了三者的底层哲学完全不同。C 的核心是"信任程序员":你 malloc 了就要自己 free,数组越界是你的问题。C++ 在此基础上做了一部分补救:RAII(Resource Acquisition Is Initialization)可以让析构函数自动释放资源,unique_ptr、shared_ptr 提供了有限度的自动内存管理,但悬垂引用、迭代器失效、多线程数据竞争这些问题仍然大面积存在。Rust 的核心是"编译器不信任任何人":所有权(ownership)、借用(borrowing)、生命周期(lifetime)这套规则是语言的一部分,违反规则根本编译不过。
这种根源差异会在项目的每个阶段体现出来。C/C++ 项目里,内存问题往往在测试阶段甚至上线后才暴露;Rust 项目里,同样的错误在编码阶段就被编译器按住了。
1.2 一个典型选型场景:资源受限的嵌入式网关
举个例子,我们之前做一个边缘计算网关,设备端是 ARM Cortex-A 系列处理器,内存只有 512MB,要跑 Modbus 数据采集、MQTT 上报、OTA 升级、本地规则引擎。团队里有人熟悉 C,有人熟悉 C++,还有人正在学 Rust。
如果选 C,开发速度快,任何芯片厂商的 SDK 都是 C 接口,但每一条数据链路都要自己检查缓冲区长度、自己管理连接句柄,一旦设备部署到现场出了问题,远程排查的代价比开发时多花的时间高一个数量级。如果选 C++,可以用现代特性写出相对安全的代码,但交叉编译工具链、动态库版本、标准库在板子上的裁剪,每一件都够喝一壶。如果选 Rust,交叉编译和部署反而省心,因为 Rust 默认静态链接,编译出来的二进制丢到板子上就能跑,而且编译器会在编译期检查出大量并发和内存问题。最终我们选了 Rust,实际开发中最大的开销是学习曲线,而不是项目本身的问题。
这类场景决定了两种语言的定位差异:C/C++ 是"与硬件和存量生态对话"的语言,Rust 是"从零开始构建可信系统"的语言。选型之前先问自己:项目要继承多少存量 C/C++ 代码?安全性和长期维护成本在不在核心需求里?这两个问题的答案,基本决定了方向。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 内存管理的三套哲学:手动、RAII 与所有权的正面碰撞
2.1 C 语言的手动管理:自由越大的地方,坑越深
C 语言的内存管理是最原始也最灵活的:malloc/free 随你调用。但这种自由是要付账的。我见过太多生产事故:一个网络服务收到恶意构造的报文,strcpy 直接写出缓冲区,导致远程代码执行;一个后台服务跑了两周,因为某条错误路径忘了 free,内存稳定增长最终 OOM;一个多线程模块里两个线程同时 free 同一个指针,直接 double free 崩溃。这些问题的共同点是:不是"会不会发生"的问题,而是"什么时候发生"的问题。
C 项目普遍的兜底手段是 Valgrind、AddressSanitizer(ASAN)、UndefinedBehaviorSanitizer(UBSAN)。这些工具确实有效,但它们都是"事后检查"。ASAN 能告诉你哪一行越界了,可它拦不住已经发布到生产环境的二进制。更麻烦的是,有些内存错误在特定优化级别、特定硬件上才复现,调试起来非常耗时。C 项目里,排查一轮内存问题花掉几天甚至几周时间,是再正常不过的事情。
2.2 C++ 的 RAII 解决了资源释放,但没解决所有权
C++ 在 C 的基础上往前迈了一大步。RAII 意味着资源在构造时获取、在析构时释放,异常安全也依赖这个机制。std::unique_ptr 表达独占所有权,std::shared_ptr 提供引用计数共享所有权,std::lock_guard 保证锁一定被释放。这个设计可以堵住大部分资源泄漏问题。
但它有自身的天花板。第一,shared_ptr 会循环引用:两个对象互相持有对方的 shared_ptr,引用计数永远不会归零,内存照样泄漏,你必须手动改用 weak_ptr 打断环。第二,shared_ptr 的引用计数本身线程安全,但它指向的对象并不是,两个线程同时通过 shared_ptr 修改同一个对象,照样数据竞争。第三,C++ 里你仍然可以把裸指针从某个函数里掏出来继续用,编译器不会拦你,悬垂引用说产生就产生。C++ 的做法是"提供工具让你写好代码",Rust 的做法是"设计语言让你写不出坏代码"。
我刚从 C++ 切到 Rust 时最不适应的就是这一点:Rust 里想搞一个"多个地方都能访
