“本文是对今天 Reddit 热帖:何时会考虑使用 Cpp 而非 Rust(Considering C++ over Rust)[1]中精彩讨论的总结。Reddit 上面讨论非常激烈,本文只是一些精彩评论的摘录,大家可以去原帖阅读更多讨论。
一位同时使用过 Rust 和 Cpp 的开发者,他用 Rust 主要是实现 Web 服务器和命令行工具,而 Cpp 则用于游戏开发(虚幻引擎)和编写虚幻引擎插件。
最近他的一个使用 Javascript 的朋友问他:“你为什么要用 Cpp,它很糟糕,Rust 解决了 Cpp 的所有问题”。
这是Rust社区一直在使用的主要口号之一。
公平地说,这并不是这位开发者开始使用Rust的原因之一,他选择使用 Rust是因为 Rust 的包管理工具 Cargo;另外一个原因是Node的创始人在谈论Deno(用Rust编写的Node.js的继任者)时说:“我再也不会在我的生命中开始一个新的C++项目了”。
另一方面,他多年来一直在使用Cpp,主要是与虚幻引擎密切配合,但他从未遇到过通常由 Rust 社区列出的问题。Cpp 有智能指针,我觉得现代 Cpp 解决了很多被认为是 Cpp 弱点的问题。这主要取决于你是什么样的程序员,以及你在其中的经验。
所以,他在 Reddit Rust 和 Cpp 频道都发了同一个帖子,想了解大家何时会选择使用 Cpp 而不是 Rust ?
sird0rius:
我讨厌 Cpp 变得如此复杂庞大。Cpp 是我20多年前学的第一门语言,我尝试学习一些更现代的写法,但由于一些只有多年经验才能学到的语言陷阱,我仍然无法达到通过一些基本的 Cpp 面试的水平。或者通过阅读比《战争与和平》还长的这个庞然大物。作为一个职业选择,对我来说学习 Rust 更有意义,因为我在进入底层编程方面比追赶 Cpp 标准更具竞争力。我认为,在学习基本的现代 Cpp 所需的时间里,我可以掌握 Rust 以及其他几种编程语言。每当我了解 Cpp 的新知识时,我都会想:“为什么在 Cpp 中做这个事情要比在 Rust 中复杂得多呢?(例如移动和复制语义)”。即使 Rust 没有大热,我仍然可以依靠我在短时间内学到的其他语言。
pr06lefs:
我曾经是一名 Cpp 开发者,有15年的经验。我现在仍然在做一些 Cpp 的工作,但是我更喜欢 Rust。Cargo是一个很重要的原因,其他函数式语言的便利也是,比如 match(而不是switch),没有异常,以及一切都是表达式而不是语句。另外一个问题是 Cpp 中错误信息的质量很差。我经常不得不滚动页面很长时间才能找到错误的实际位置。错误信息并不总是越多越好。使用一个不错的 Cpp 子集进行编码是可以的,但并不是每个人对于什么是这个不错的子集都有相同的理解。有些人总是会探索可能性的边界,只为了提升自己的技能。当你在20万行代码中的某个地方遇到一个无法重现的核心转储,或者一个只在调试模式下工作而在发布模式下不工作的大型程序时,这真是令人沮丧。追踪那个因为数组操作错误导致的偏移量问题或其他问题可能非常耗时。这是一类令人讨厌的问题,在安全的Rust代码中根本不会出现。这就为我们留下了更多时间来解决有趣且有生产力的问题。
TheReservedList:
与作者相同的开发背景:游戏开发,工作中使用Cpp;个人项目用的是Rust。
phazer99 :
这是百分之百正确的。当你在团队中工作时,你会真正欣赏到Rust 及其工具相对于 Cpp(以及其他语言)所提供的优势。现在我在一个由所有Rust初学者组成的团队中,他们正在处理一个相当庞大的代码库,但代码仍然非常统一、易于理解和维护。Rust 编译器(以及Clippy)真正强制/鼓励每个人遵循良好的习惯和编码风格。很少有 unsafe 代码,因为每个人都知道涉足这个领域的“危险”。Cargo 还使处理内部和外部依赖变得轻而易举。
sayhisam1:
就是这样。Cpp 有选择性的安全性,我发现在实践中这真的很困难。有没有一本简短、易于记忆的“白痴安全Cpp”类型的书,我可以参考一下?即使有了这样的书,我也要自己确保不会意外地写出一些不安全的代码。在Rust中,安全代码是默认的;你必须明确地将其包装在 unsafe 的代码块中,并且必须意识到这一点。在不安全的区域之外,我几乎可以保证不会出现使用后释放错误或类似的问题。Rust 的风格也更加一致,因为标准库更加合理,教程也非常棒。
zoomy_kitten:
模板是90年代的一个支撑,旨在满足开发者在普通静态多态和普通元编程方面的需求。Rust 的泛型和宏是我选择 Rust 而不是 Cpp 的原因之一。毫无疑问,模板是一个强大的工具,尤其是在当时。别误会,我并不是要冒犯 Cpp,它是一门了不起的语言,如果没有 Rust,我可能会更经常地使用它。但是与 Rust 提供的功能相比,如今它的特性相当可怜。Cpp 是一门靠支撑物生存的语言,而 Rust 是一门拥有健康双腿的语言。
Orthosz:
Cpp 已经存在了大约 40 年。其他具有“现代”方式但保留了类似时间的旧东西的语言也有大量糟糕的教程。我敢打赌,在未来的30年里,Rust 会有糟糕的教程,教授一种混合了旧方式、新近方式和新方式的内容,因为 Rust,就像 Cpp、Java和其他系统语言一样,承诺向后兼容。
TheReservedList > Orthosz:
永恒的向后兼容性,就像 Cpp 那样,从长远来看永远行不通,每一种语言都注定会失败。起初我以为这就是版本的意义,但似乎对于这样一门年轻的语言来说,改变已经存在很多犹豫。
Orthosz:
这将让每个人都感到不舒服,而且应该如此:constexpr、模板元编程和预处理器宏让你以一种弗兰肯斯坦怪物的方式做出真正令人惊叹和可怕的事情。
harmic:
另一个关键因素:在许多情况下,C++编译器的错误信息非常糟糕。想象一下,从你错误使用的某个模板库深处涌出一大堆无意义的内容。Rust编译器的错误信息通常非常出色,甚至经常提供修复问题的建议。
dkopgerpgdolfg:
不,Rust并不能解决所有问题。我猜写一部分现代C++代码可能会很不错。但这并不是我来到Rust营地的主要原因。相反,最迫切的原因是因为"Cpp"并不等同于"现代Cpp的子集"。如果我能“写”出漂亮的Cpp,但人们仍然能够写出280页的书来解释变量初始化,那对我有什么帮助呢?代码也需要被“阅读”。包括那些不符合个人偏好的代码。Cpp确实非常有用。但它也变得非常复杂,有时候我不明白为什么我要继续朝这个方向发展——越来越多的时间花在查找标准、缺陷报告等符号上,而不是编写有用的代码。Rust 也不完全摆脱这个问题,但两者之间有天壤之别。
KnorrFG:
在Rust出现之前,C++曾经是我首选的编程语言。不过现在我不再使用它了,除非是在嵌入式领域。我从来没有在内存管理方面遇到太多困难,但项目配置和依赖关系真是让人烦恼。我使用过makefiles、scons、cmake和Visual Studio,它们各有各的烦人之处。尤其是与Cargo相比。总的来说,使用包含方式处理多个文件是一种糟糕的方法。我仍然记得花了两天时间搜索一个错误,因为一个头文件中的定义覆盖了一个无关依赖项中的函数。我真的很烦迭代器的管理。到处都是.begin()
和.end()
...太丑陋而啰嗦了。哦,还有模板错误...然后在Rust中有一些非常吸引人的东西:代数数据类型、问号运算符、Cargo以及大部分自动类型推断。单独来看,这些都不算什么,但是所有因素的结合使得 Rust 对我来说显然更具吸引力。
adwhit2:
我不明白为什么有人经历过带有和类型的语言后,还会愿意回到没有和类型的语言。这就像是用一只手绑在背后进行编码。
C++现在有std::variant了,但是在人机交互方面,它与Rust的枚举和模式匹配相比还有很大差距。
std::visit
是 match
的一个糟糕的替代品。最具体地说,在一个 match
的情况下,我可以 continue
/ break
/ return
,因为这个 match
与 match
处于相同的函数上下文中...然而,在 std::visit
中的lambda函数中,我不能...因此,我需要将continue/break/return重新定义为一个结果值(或标志),然后在 std::visit
之后根据它进行分支。当人们赞扬某种类型时,他们实际上赞扬的是模式匹配 :)
safdwark4729:
Rust目前缺乏特化和反向类型 trait,可以说这是Rust与Cpp不同的一个差距,类似于和许多其他Rust特性一样。当然,我仍然认为实际的泛型是比模板更好的选择,只是恰好这些特性对于高性能代码和甚至安全性(单位……你现在在Rust中很难做到这一点,也有其他原因)非常重要,并且在库生态系统和常量泛型之后,它们是阻止我在Rust中做更多事情的最大问题之一。
忽略自身不断完善的库生态系统,Rust 需要(或等效功能/或使其变得无关):
据我所知,所有这些都正在进行中/将来的工作计划中,但其中一些我现在需要,就像现在这样,并且与Rust的愿景相一致。
randompittuser:
我已经使用C++二十年了,Rust使用了一年。我可能会因为我的观点而遭受r/rust的愤怒,但是我还是要说:对于有经验的 Cpp 开发者来说,Rust并没有太多吸引力,但并非每个人都是有经验的 Cpp 开发者。Rust 的最大优势之一是将许多运行时/内存错误转移到编译时。为了实现这一点,与 Cpp 相比,它对类型及其使用的假设进行了限制,使得 Rust 更冗长(尽管可能更具表达力),尤其是在高级用例中。然而,请考虑到 Rust 相对于 Cpp 还处于起步阶段,我相信在未来十年内,它有潜力超越 Cpp,在新项目中得到更广泛的应用。考虑到这一切,我会说学习两者都是不错的选择。
msqrt:
我曾经简单尝试过 Rust。确实,知道编译器可以在很多错误发生之前就捕捉到它们,这让人感到非常放心。但就个人而言,这并不足以让我放弃我已经相当熟悉的语言,转而去学习一门需要付出相当努力的新语言。我觉得C++在不久的将来可能会失去很多地位,但我并不急于成为这个过程中的早期采用者。我会先看看情况如何发展。
Sudden_Job7673:
C++面临的一个重大挑战是,在互联网时代之后,它的发展机制无法像其他生态系统那样快速改进。缺乏一个能够在编译时检测到可能导致错误的机制,对于什么是C++的惯用方式缺乏共识(是否包括异常处理?),缺乏一个被广泛采用的不稳定C++版本来尝试新的理念,以及二进制稳定性使得一旦某个特性进入标准后很难进行修正。
jgaa_from_north :
变化的速度和范围是我现在更加关注 Rust 的原因之一。这是我暑假的“项目”。当C++发生变化时,你被认为应该理解新的方式和旧的方式。你需要了解的内容总量在这个已经相当复杂的语言中以很高的速度增加。我并不羡慕那些今天学习C++的人。
此外,这些变化并未解决C++中一些最紧迫的问题,比如缺乏标准的构建系统、标准的软件包管理器或仓库基础设施。当一个项目不断发展时,管理常常冲突的递归依赖关系是一种痛苦。自1996年以来,我一直将C++作为我的主要编程语言。我可能已经写了超过一百万行的C++代码。我仍然比我使用过的任何其他编程语言更喜欢C++。我的观点是,标准中的变化/创新是一把双刃剑。
guyinnoho:
Rust 是较新的。再给它十年,然后再比较。
dzordan33:
在 Rust 生态系统中,低门槛是一个巨大的优势。在我看来,雇佣那些能够并愿意学习 Rust 的聪明程序员要比经验丰富的 C++ 开发者更容易。根据我的经验,你不必是一个 Rust 专家才能高效工作。
darth_chewbacca :
你好,我来自这个讨论主题的 Rust 版块。请不要恶意攻击。我曾经是一名 C++ 开发者,最后接触的版本是 C++17。我最喜欢的语言是 Rust,但我仍然喜欢 C++。
对于一个熟悉“现代C++”并且是资深C++开发人员来说,学习Rust并不太困难。在之前的雇主那里,我帮助培训了几位C++开发人员(还有一位经验丰富的Java开发人员),他们学习起来并不困难。但是C开发人员、C#开发人员以及Python/JavaScript开发人员确实遇到了一些困难。
C++和Rust比大多数其他广泛使用的编程语言更相似。现代C++在用法上可能比原始的C更接近Rust。对你来说,学习曲线可能没有其他尝试Rust的人那么陡峭。
当你听到关于Rust学习曲线的抱怨时,这些抱怨通常不是来自以前的C++开发者。
nihilistic_ant:
很长一段时间里,Java一度被认为会取代C++。Sun公司用Java编写了一个操作系统,而Netscape则将他们的浏览器移植到了Java上。后来,我记得有一段时间Go语言被认为会取代C++,但回想起来,这种说法并没有多大意义,尽管当时看起来似乎很有道理。除了这些大型语言之外,还有很多其他流行的语言,人们常常问为什么项目选择使用C++而不是它们,尽管它们当时很热门,比如D语言或者Scala,虽然现在它们已经被人们遗忘,但在一段时间内它们占据了很多人的关注。
也许Rust会真的做到。也许Zig会。也许几年后会有一些东西开始。很难说。
总的来说,我不羡慕那些总是追逐最新编程语言和框架的程序员。(几年前对他们来说尤其艰难,因为他们不得不在短短几年内学习了大约6个JavaScript框架,以跟上潮流。)他们的代码最终变得难以维护,因为会有各种用不再流行的语言或框架编写的系统存在。
无论如何,如果Rust或Zig真的能够主宰世界,我会很高兴转向它们。但对我个人来说,等待几年看看是否真的如此是有道理的。
像Rust这样的新语言的一个问题是,它们的用户都是决定使用最新、最酷的语言的程序员。这意味着,如果有什么新东西出现,他们的用户很可能会转投他。而那些仍然使用C++进行编程的人在很多次之前都选择了不转投其他语言,所以我相信这门语言至少会在相当长的时间内相当受欢迎。
laralex:
我对C++的三大痛点是:
monsoon_winds:
哥们儿,现代C++可以避免老版本C++的问题,但只要向后兼容性存在,老版本C++仍然存在。在编写C++代码时,你需要一直积极地避免这些问题,直到你的生命结束。而Rust则默认提供了你所需要的,你需要主动选择进入不安全模式,才会出现问题。
把自己(或任何有经验的程序员)看作是一个无法写出任何错误的编程之神,这种想法实在是愚蠢的。我会在这里链接到cpp2的演讲[2],Herb在其中用简单的话解释了这个问题。
正如你所提到的,shared_ptr和一些现代特性确实有所帮助,但它仍然只是对这个巨大的UB潜在问题的临时解决办法。即使是像Cherno(著名的YouTuber)这样经验丰富的游戏引擎开发者,也不得不花费数小时进行调试,最终才发现在一个for-each循环中,他们无效化了迭代器 :D
你对“有经验的程序员”的定义也不具体可行。如果谷歌/微软这样拥有庞大预算的公司都无法保持软件质量,那么其他随机公司又有什么机会呢?
tiajuanat:
请记住,我是一个有7年最近活跃的C++编程经验的新手,但我几乎一生都在以某种方式进行编程。
我有些犹豫,但不得不说Rust自带了许多很棒的功能,而C++在这方面还远远无法媲美。Conan、CMake、Doxygen、Catch/Doctest/GTest,Rust都有自己的系统来处理这些,而且还有更多其他功能。
有一些地方是痛苦的。所有权并不总是一帆风顺的,在与其他C++开发人员合作编写Rust代码时,会出现“正确方式”和“Rust方式”的争论。上个月,我与人合作开发串行协议时,弄清楚哪个系统“拥有”串行端口并不容易,最后使用了一个奇怪的弱指针。
Rust有一些不完美的地方吗?绝对有。事实上,我认为没有这些不完美的地方,你就无法拥有一种适用于工业系统的编程语言。然而,我很少感觉到与Rust 的斗争,而且我已经积极地使用 Rust 几个月了;而在C++中,我花了七年的时间才达到这个水平。
你不能只是把一本 Rust书放在开发人员手上,希望通过知识渗透让他们学习到 Rust 所有权。
[1]
Reddit 热帖:何时会考虑使用 Cpp 而非 Rust(Considering C++ over Rust): https://www.reddit.com/r/rust/comments/16bkx6b/considering_c_over_rust/
[2]
cpp2的演讲: https://youtu.be/ELeZAKCN4tY?t=2618