我的征尘是星辰大海。。。
The dirt and dust from my pilgrimage forms oceans of stars...
-------当记忆的篇章变得零碎,当追忆的图片变得模糊,我们只能求助于数字存储的永恒的回忆
作者:黄教授
手机视频列表
编程语言该不该禁止程序员朝自己的脚开枪
视频
音频
原始脚本
编程语言到底该不该阻止程序员朝自己脚上开枪?深度复盘 C C 加加与 Rust 的终极路线对错。 编程圈一直有一个最经典也最扎心的终极命题。 编程语言到底要不要彻底禁止程序员写出危险代码?彻底杜绝内存错误?彻底锁死所有未定义行为?Rust 用绝对强硬的方式给出了答案。 要,而且必须在语言底层、编译阶段一刀切,彻底禁止。 而 C C 加加数十年不变的底层哲学恰恰相反,我不阻止你犯错。 我只给你最强的能力,安全你可以自己管,边界我不替你锁死。 绝大多数对比文章都在纠结谁更安全,谁性能更好。 谁 bug 更少?但真正的核心问题从来不是安全与否,而是一门语言,如果为了防止普通人犯错,彻底锁死所有危险操作。 它是不是在同时锁死了高阶开发、锁死了极端场景、锁死了技术突破的可能性?答案很明确,是的。 过度绝对化语言及强制安全。 本身就是一种技术枷锁。 一、用一个极端现实案例,击穿绝对安全的逻辑谬误。 我们可以用最通俗、最精准的警察与人质案例。 彻底讲透安全规则的局限性。 所有正规警察手册、执法规范都有一条绝对铁律,严禁向无辜人质开枪。 在99%的常规场景里,这条规则是对的,是保护生命、规避风险的底线。 但存在极少数极端场景,恐怖分子挟持人质,以人质为物理盾牌,逼迫执法方妥协。 进一步实施爆炸、屠杀、公共安全灾难。 此时死守不准射杀人质的安全教条,只会导致更大规模的灾难、更大的牺牲。 更彻底的失控。 在这种极端工况下,打破安全规则反而是最优解、最小代价解、唯一破局解。 编程安全完全一模一样。 所谓内存安全、类型安全、借用安全、不可变安全,全部都是常规场景的通用教条。 他们可以杜绝99%的新手 bug 团队 bug 业务 bug 单系统底层、逆向工程、闭源适配、二进制魔改。 老旧驱动兼容、裸机硬件调试全部属于那1%的极端工况。 在这些场景里,常规安全规则就是最大的障碍。 死守语言级绝对安全不是更稳,而是啥事都干不成。 这就是 C C 加加与 Rust 的根本分歧。 Rust 为了100%杜绝普通人犯错。 直接废掉那1%的极端突破能力。 C C 加加,99%场景你可以守规矩,但我保留你突破规则的权利。 二、真正的高阶开发必须拥有朝自己开枪的权利。 计算机界那句老话,很多人只听懂了一半。 C 语言不阻止你朝自己脚上开枪,多数人理解为 C 不安全,容易炸。 容易出 bug 真正的深层含义是高级技术、底层突破、边界探索、非常规适配,本身就需要突破常规规则。 禁止犯错等于禁止探索。 新手写野指针是 bug 高手操控裸内存,做类型双关,做内存布局伪造,做强制转换。 是解决无解问题的唯一手段。 最真实、最无可辩驳的例子,逆向工程、闭源动态库适配,实际工程场景非常普遍。 我们手上只有第三方编译好的点 so 斜杠点 dll 动态库,无源码、无头文件、无文档、无结构体说明,我们只能通过符号表看到。 存在某个类,某个公开成员函数,且我们必须调用这个函数完成功能对接。 但所有 C 加加类成员函数调用前置条件只有一个。 必须拥有一个合法的类实例对象。 问题来了,无头文件。 我不知道类大小,不知道内存布局,不知道成员结构。 正规、安全、标准的写法。 完全无法创建对象。 如果是 Rust 到此结束,彻底无解,安全规则直接锁死一切可能性。 但在 C C 加加里,高阶工程师有唯一的试探方案。 手动申请一段足够尺寸的内存数组,强行将整片裸内存强制类型转换为目标类对象,人工伪造一个合规实例,再直接调用其成员函数。 这个行为极度危险,完全不规范,完全不符合现代安全标准,随时可能崩溃、栈失衡、内存错乱。 但这是无头文件逆向适配场景下唯一可行、唯一能尝试出结果的路径。 这种操作就是所谓的朝自己开枪。 但在极端工程困境里,敢开枪、能开枪、有枪可开,就是唯一出路。 Rust 把这把枪没收了,它保护了你不会受伤,也彻底废掉了你绝境破局的能力。 三、为什么安全绝对不能写入语言底层?不能强制全员统一。 现在很多人鼓吹 Rust 的正确性,认为未来语言就应该全员编译器安全、全员禁止未定义行为、全员强制借用检查。 这里存在一个巨大的认知误区。 安全是工程规范,不是语言宪法。 安全完全可以通过外部手段百分之一百实现。 静态分析工具 VeriGuard 静态检测、企业编译开关到团队代码子集规范、 CI 强制检查、编码规约、禁用危险语法。 也就是说,引以为傲的所有安全能力, C C 加加都可以通过工具层、规范层按需实现。 重点是按需。 普通业务项目、团队协作项目、稳定性优先项目,可以全开安全限制,收紧语法,禁止裸操作,杜绝风险。 底层逆向、内核调试、二进制适配、老旧硬件项目可以完全放开限制,保留所有底层自由度。 这才是最科学的工程模型。 安全可插拔,能力永久保留。 Rust 的模型是反过来的,安全永久绑定,能力按需阉割。 Unsafe。 而且它的阉割是结构性的、语言级的、不可逆的。 四、历史早已证明,强约束语言必有天花板。 早年,Delphi Pascal 就是最好的前车之鉴。 Pascal 当年设计理念极其先进,无裸指针、强类型安全、语法严谨、完全防错,对新手极其友好,几乎不会写出内存错误。 但它的代价极其惨烈。 自定义调用约定、自定义栈规则、参数压栈顺序与 C 系统 API 不兼容。 越学到深处,越发现语言规则限制了一切底层操作。 最终结局适合入门,不适合深耕,适合应用,不适合底层,慢慢沦为小众工具。 Rust 的生命周期。 借用检查默认不可变,走的是一模一样的路。 新手爽,中级稳,高阶痛,深度开发直接束手束脚。 五、终局判断。 Rust 永远成不了通用语言,只能是专用工具。 总结全部逻辑。 一句话定终局,一门语言,如果为了杜绝低级错误,剥夺了人类突破工程极限的能力。 它就只配做小众专用工具,不配做通用底层语言。 Rust 不会死,它在高安全服务器、浏览器组件、防漏洞模块里非常合适。 因为这些场景不需要突破边界,只需要死守规则。 但系统底层、逆向、安全研究、驱动适配、老旧生态。 二进制魔改,永远不属于 Rust 而 C C 加加的长盛不衰,本质就是因为它守住了一个顶级设计哲学,规则是人定的,场景是无限的。 语言不锁边界,人才有上限。 六、最终结论,编程语言到底该不该阻止程序员朝自己脚上开枪?答案是可以引导,可以警告,可以规范。 可以工具限制,但绝对不能在语言内核彻底静止。 绝对化的语言安全,消灭的不是 bug 而是极端场景的解决能力。 底层技术的探索空间、工程师的操作自由度。 安全是重要的,但安全不是一切。 为了安全废掉可能性,是编程语言设计最大的短视。 这就是 C C 加加永远不会全面拥抱 Rust 的是强约束,也永远不会被 Rust 替代的终极真相。
修正脚本
编程语言到底该不该阻止程序员朝自己脚上开枪?深度复盘 C C 加加与 Rust 的终极路线对错。 编程圈一直有一个最经典也最扎心的终极命题。 编程语言到底要不要彻底禁止程序员写出危险代码?彻底杜绝内存错误?彻底锁死所有未定义行为?Rust 用绝对强硬的方式给出了答案。 要,而且必须在语言底层、编译阶段一刀切,彻底禁止。 而 C C 加加数十年不变的底层哲学恰恰相反,我不阻止你犯错。 我只给你最强的能力,安全你可以自己管,边界我不替你锁死。 绝大多数对比文章都在纠结谁更安全,谁性能更好。 谁 bug 更少?但真正的核心问题从来不是安全与否,而是一门语言,如果为了防止普通人犯错,彻底锁死所有危险操作。 它是不是在同时锁死了高阶开发、锁死了极端场景、锁死了技术突破的可能性?答案很明确,是的。 过度绝对化语言及强制安全。 本身就是一种技术枷锁。 一、用一个极端现实案例,击穿绝对安全的逻辑谬误。 我们可以用最通俗、最精准的警察与人质案例。 彻底讲透安全规则的局限性。 所有正规警察手册、执法规范都有一条绝对铁律,严禁向无辜人质开枪。 在99%的常规场景里,这条规则是对的,是保护生命、规避风险的底线。 但存在极少数极端场景,恐怖分子挟持人质,以人质为物理盾牌,逼迫执法方妥协。 进一步实施爆炸、屠杀,造成公共安全灾难。 此时死守不准射杀人质的安全教条,只会导致更大规模的灾难、更大的牺牲、更彻底的失控。 在这种极端工况下,打破安全规则反而是最优解、最小代价解、唯一破局解。 编程安全完全一模一样。 所谓内存安全、类型安全、借用安全、不可变安全,全部都是常规场景的通用教条。 他们可以杜绝99%的新手 bug、团队 bug、业务 bug,但单系统底层、逆向工程、闭源适配、二进制魔改。 老旧驱动兼容、裸机硬件调试全部属于那1%的极端工况。 在这些场景里,常规安全规则就是最大的障碍。 死守语言级绝对安全不是更稳,而是啥事都干不成。 这就是 C C 加加与 Rust 的根本分歧。 Rust 为了100%杜绝普通人犯错。 直接废掉那1%的极端突破能力。 C C 加加,99%场景你可以守规矩,但我保留你突破规则的权利。 二、真正的高阶开发必须拥有朝自己开枪的权利。 计算机界那句老话,很多人只听懂了一半。 C 语言不阻止你朝自己脚上开枪,多数人理解为 C 不安全,容易炸。 容易出 bug,真正的深层含义是高级技术、底层突破、边界探索、非常规适配,本身就需要突破常规规则。 禁止犯错等于禁止探索。 新手写野指针是 bug,高手操控裸内存,做类型双关,做内存布局伪造,做强制转换。 是解决无解问题的唯一手段。 最真实、最无可辩驳的例子,逆向工程、闭源动态库适配,实际工程场景非常普遍。 我们手上只有第三方编译好的点 so 斜杠点 dll 动态库,无源码、无头文件、无文档、无结构体说明,我们只能通过符号表看到。 存在某个类,某个公开成员函数,且我们必须调用这个函数完成功能对接。 但所有 C 加加类成员函数调用前置条件只有一个。 必须拥有一个合法的类实例对象。 问题来了,无头文件。 我不知道类大小,不知道内存布局,不知道成员结构。 正规、安全、标准的写法。 完全无法创建对象。 如果是 Rust 到此结束,彻底无解,安全规则直接锁死一切可能性。 但在 C C 加加里,高阶工程师有唯一的试探方案。 手动申请一段足够尺寸的内存数组,强行将整片裸内存强制类型转换为目标类对象,人工伪造一个合规实例,再直接调用其成员函数。 这个行为极度危险,完全不规范,完全不符合现代安全标准,随时可能崩溃、栈失衡、内存错乱。 但这是无头文件逆向适配场景下唯一可行、唯一能尝试出结果的路径。 这种操作就是所谓的朝自己开枪。 但在极端工程困境里,敢开枪、能开枪、有枪可开,就是唯一出路。 Rust 把这把枪没收了,它保护了你不会受伤,也彻底废掉了你绝境破局的能力。 三、为什么安全绝对不能写入语言底层?不能强制全员统一。 现在很多人鼓吹 Rust 的正确性,认为未来语言就应该全员编译器安全、全员禁止未定义行为、全员强制借用检查。 这里存在一个巨大的认知误区。 安全是工程规范,不是语言宪法。 安全完全可以通过外部手段百分之一百实现。 静态分析工具 VeriGuard 静态检测、企业编译开关到团队代码子集规范、 CI 强制检查、编码规约、禁用危险语法。 也就是说,引以为傲的所有安全能力, C C 加加都可以通过工具层、规范层按需实现。 重点是按需。 普通业务项目、团队协作项目、稳定性优先项目,可以全开安全限制,收紧语法,禁止裸操作,杜绝风险。 底层逆向、内核调试、二进制适配、老旧硬件项目可以完全放开限制,保留所有底层自由度。 这才是最科学的工程模型。 安全可插拔,能力永久保留。 Rust 的模型是反过来的,安全永久绑定,能力按需阉割。 Unsafe。 而且它的阉割是结构性的、语言级的、不可逆的。 四、历史早已证明,强约束语言必有天花板。 早年,Delphi Pascal 就是最好的前车之鉴。 Pascal 当年设计理念极其先进,无裸指针、强类型安全、语法严谨、完全防错,对新手极其友好,几乎不会写出内存错误。 但它的代价极其惨烈。 自定义调用约定、自定义栈规则、参数压栈顺序与 C 系统 API 不兼容。 越学到深处,越发现语言规则限制了一切底层操作。 最终结局适合入门,不适合深耕,适合应用,不适合底层,慢慢沦为小众工具。 Rust 的生命周期。 借用检查默认不可变,走的是一模一样的路。 新手爽,中级稳,高阶痛,深度开发直接束手束脚。 五、终局判断。 Rust 永远成不了通用语言,只能是专用工具。 总结全部逻辑。 一句话定终局,一门语言,如果为了杜绝低级错误,剥夺了人类突破工程极限的能力。 它就只配做小众专用工具,不配做通用底层语言。 Rust 不会死,它在高安全服务器、浏览器组件、防漏洞模块里非常合适。 因为这些场景不需要突破边界,只需要死守规则。 但系统底层、逆向、安全研究、驱动适配、老旧生态、二进制魔改,永远不属于 Rust,而 C C 加加的长盛不衰,本质就是因为它守住了一个顶级设计哲学,规则是人定的,场景是无限的。 语言不锁边界,人才无上限。 六、最终结论,编程语言到底该不该阻止程序员朝自己脚上开枪?答案是可以引导,可以警告,可以规范。 可以工具限制,但绝对不能在语言内核彻底禁止。 绝对化的语言安全,消灭的不是 bug,而是极端场景的解决能力、底层技术的探索空间、工程师的操作自由度。 安全是重要的,但安全不是一切。 为了安全废掉可能性,是编程语言设计最大的短视。 这就是 C C 加加永远不会全面拥抱 Rust 这种强约束,也永远不会被 Rust 替代的终极真相。
英文翻译
Should programming languages prevent programmers from shooting themselves in the foot? A deep review of the ultimate right and wrong paths of C, C++, and Rust. The programming community has always had a most classic and poignant ultimate proposition: Should programming languages completely prohibit programmers from writing dangerous code? Completely eliminate memory errors? Completely lock down all undefined behaviors? Rust gives an answer in an absolutely强硬 manner: Yes, and it must be一刀切 at the language bottom and compilation stage, completely prohibiting them. C and C++, on the other hand, have adhered to a decades-long underlying philosophy that is exactly the opposite: I won't stop you from making mistakes. I only give you the strongest capabilities; you can manage safety yourself, and I won't lock down boundaries for you. Most comparison articles obsess over which is safer, which has better performance, or which has fewer bugs. But the real core issue has never been about safety or not—it's about whether a language, in order to prevent ordinary people from making mistakes, completely locks down all dangerous operations. Is it simultaneously locking out high-level development, extreme scenarios, and the possibility of technological breakthroughs? The answer is clear: yes. Overly absolutist language safety and mandatory safety is itself a technological shackle. 1. Use an extremely realistic case to break through the logical fallacy of absolute safety. We can use the most intuitive and precise police-hostage case to thoroughly explain the limitations of safety rules. All official police manuals and law enforcement codes have an absolute iron rule: firing at innocent hostages is strictly prohibited. In 99% of routine scenarios, this rule is correct—it's the baseline for protecting life and avoiding risk. But there exist a very small number of extreme scenarios: terrorists take hostages, using them as physical shields to force law enforcement to compromise, then proceed with bombings, massacres, causing public safety disasters. At such times, sticking to the safety dogma of not shooting hostages would only lead to larger disasters, greater sacrifices, and more complete loss of control. In such extreme situations, breaking the safety rule is actually the optimal solution, the least-cost solution, and the only way to break the deadlock. Programming safety is exactly the same. So-called memory safety, type safety, borrowing safety, immutability safety—all are universal dogmas for routine scenarios. They can eliminate 99% of novice bugs, team bugs, and business bugs. But system-level low-level development, reverse engineering, closed-source adaptation, binary modification, legacy driver compatibility, and bare-metal hardware debugging all belong to that 1% of extreme conditions. In these scenarios, routine safety rules become the biggest obstacle. Sticking to language-level absolute safety is not more stable—it makes everything impossible. This is the fundamental divergence between C/C++ and Rust: Rust, in order to 100% prevent ordinary people from making mistakes, directly abandons that 1% of extreme breakthrough capability. C/C++ says: in 99% of scenarios you can follow the rules, but I reserve your right to break the rules. 2. True high-level development must have the right to shoot yourself in the foot. The old saying in the computer world—many people only understand half of it. "C language doesn't stop you from shooting yourself in the foot." Most people interpret it as: C is unsafe, easy to crash, easy to have bugs. The real deep meaning is that advanced technology, low-level breakthroughs, boundary exploration, and unconventional adaptation inherently require breaking conventional rules. Prohibiting mistakes equals prohibiting exploration. Novices write dangling pointers as bugs; experts manipulate raw memory, perform type punning, forge memory layouts, and do forced casts—these are the only means to solve unsolvable problems. The most real and undeniable example: reverse engineering, closed-source dynamic library adaptation—very common in actual engineering scenarios. We only have a third-party compiled .so or .dll dynamic library, no source code, no header files, no documentation, no structure descriptions. We can only see via the symbol table that there exists a class, a certain public member function, and we must call this function to complete the functional integration. But all C++ class member function calls have only one prerequisite: we must have a valid class instance object. The problem is: no header files. I don't know the class size, memory layout, or member structure. The orthodox, safe, standard way cannot create an object at all. If it were Rust, it ends here—completely unsolvable, safety rules directly lock out all possibilities. But in C/C++, a high-level engineer has the only exploratory solution: manually allocate a memory array of sufficient size, forcibly cast the entire raw memory to the target class type, artificially forge a compliant instance, and then directly call its member function. This behavior is extremely dangerous, completely non-standard, entirely against modern safety standards, and could crash, cause stack imbalance, or memory corruption at any moment. But in a no-header-file reverse engineering adaptation scenario, this is the only feasible path that can yield results. This kind of operation is what is meant by "shooting yourself in the foot." But in extreme engineering predicaments, having the courage to shoot, being able to shoot, and having a gun available is the only way out. Rust confiscates that gun—it protects you from injury but also completely cripples your ability to break out of dead ends. 3. Why safety must absolutely not be written into the language's bottom layer? Why can't it be mandatory for everyone? Nowadays many people advocate for Rust's correctness, believing that future languages should all have compiler-enforced safety, all prohibit undefined behavior, and all enforce borrowing checks. There is a huge misconception here: safety is an engineering standard, not a language constitution. Safety can be 100% achieved through external means: static analysis tools like VeriGuard, corporate compilation flags, team code subset specifications, CI mandatory checks, coding conventions, banning dangerous syntax. That is, all the safety capabilities that are touted can be implemented on demand in C/C++ through tooling and specification layers. The key is "on demand." For ordinary business projects, team collaboration projects, and projects prioritizing stability, you can fully enable safety restrictions, tighten syntax, prohibit raw operations, and eliminate risks. For low-level reverse engineering, kernel debugging, binary adaptation, and legacy hardware projects, you can completely relax restrictions and retain all low-level freedom. This is the most scientific engineering model: safety is pluggable, capability is permanently retained. Rust's model is the opposite: safety is permanently bound, capability is castrated on demand (unsafe). Moreover, its castration is structural, language-level, and irreversible. 4. History has already proven that strongly-constrained languages inevitably hit a ceiling. In earlier years, Delphi and Pascal were the best precedents. Pascal's design philosophy was extremely advanced at the time: no raw pointers, strong type safety, rigorous syntax, completely error-proof, very friendly to novices, almost impossible to write memory errors. But its cost was severe: custom calling conventions, custom stack rules, incompatible parameter push order with C system APIs. The deeper you learned, the more you found that language rules restricted all low-level operations. The final outcome: suitable for beginners, not for deep learning; suitable for applications, not for low-level work; gradually becoming a niche tool. Rust's lifetimes, borrowing checks, and default immutability are going the same path: novices are happy, mid-level is stable, high-level is painful, and deep development is directly shackled. 5. Final judgment: Rust will never become a general-purpose language; it can only be a specialized tool. Summing up all logic in one sentence to determine the outcome: If a language, in order to eliminate low-level errors, deprives humans of the ability to push engineering limits, it only deserves to be a niche specialized tool, not a universal low-level language. Rust will not die; it is very suitable for high-security servers, browser components, and vulnerability-resistant modules. Because these scenarios do not need to break boundaries—they only need to stick to rules. But system-level low-level work, reverse engineering, security research, driver adaptation, legacy ecosystems, and binary modification will never belong to Rust. And the enduring prosperity of C/C++ stems essentially from adhering to a top-tier design philosophy: rules are made by people, scenarios are infinite. The language does not lock boundaries; thus human potential has no upper limit. 6. Final conclusion: Should programming languages prevent programmers from shooting themselves in the foot? The answer is: they can guide, warn, standardize, and restrict via tools, but they absolutely must not completely prohibit it within the language kernel. Absolute language safety does not eliminate bugs—it eliminates the ability to solve extreme scenarios, the space for low-level technology exploration, and the engineer's operational freedom. Safety is important, but safety is not everything. Sacrificing possibility for safety is the greatest shortsightedness in programming language design. This is the ultimate truth of why C and C++ will never fully embrace a strongly-constrained language like Rust, and will never be replaced by Rust.
back to top