代码阅读:发现Bug的超级能力
作者分享了其职业生涯中一个重要的转变:从迭代式编程转向主动发现Bug。不再仅仅依赖测试迭代,而是通过仔细阅读代码来提前发现问题。文章指出,仔细阅读代码,构建程序的完整心智模型,并找出模型与代码的差异,是发现Bug的关键。作者建议关注控制流和数据结构,并识别代码中的潜在错误模式。通过这种方式,可以有效减少Bug,提高代码质量。
阅读更多
作者分享了其职业生涯中一个重要的转变:从迭代式编程转向主动发现Bug。不再仅仅依赖测试迭代,而是通过仔细阅读代码来提前发现问题。文章指出,仔细阅读代码,构建程序的完整心智模型,并找出模型与代码的差异,是发现Bug的关键。作者建议关注控制流和数据结构,并识别代码中的潜在错误模式。通过这种方式,可以有效减少Bug,提高代码质量。
阅读更多
作者探索如何编写一个优雅的重试循环,该循环能清晰地限制重试次数、避免最后一次尝试后的无用休眠、在重试失败时报告原始错误,且避免代码冗余。文章比较了几种方法,最终采用了一个 `try while` 循环,并通过添加循环上限来保证循环的终止,解决了之前的方案中存在的边界问题和潜在的无限循环风险。最终方案虽然仍有不完美之处,但相比之前的尝试,在简洁性和可靠性上有了显著提升。
阅读更多
Zig 语言提供了一种优雅的方式来处理枚举的偏匹配,避免了冗余代码和运行时恐慌。文章介绍了一种技巧,利用 `inline` 和 `comptime unreachable`,让编译器在编译时就能检查到不必要的 `else` 分支,从而提高代码的健壮性和可读性。这种方法尤其在处理大量枚举变体时,能够极大简化代码逻辑。
阅读更多
本文挑战了 CSS 属性 `font-size-adjust` 的普遍误解。作者指出,`font-size` 指定的是字形周围方框的大小,而非字形本身大小,不同字体字形大小差异很大。`font-size-adjust`并非仅仅用于字体回退,而是能使页面上不同字体大小更一致,作者建议将其设置为 `ex-height 0.53`,以使不同字体大小更统一,提升页面排版一致性。
阅读更多
厌倦了臃肿的RSS阅读器?作者另辟蹊径,利用Deno和一个简单的文本文件构建了自己的RSS阅读器。它只显示最新三篇文章的标题和链接,无需本地存储全文或阅读标记,通过GitHub Actions每日自动更新。代码简洁,易于理解和扩展,是一个极简主义者的福音。
阅读更多
Rust编译速度慢是众所周知的,但本文作者认为大多数Rust项目编译速度都远低于预期。以rust-analyzer为例,其20万行代码及百万行依赖项的CI流程在GitHub Actions上仅需8分钟。作者详细阐述了如何优化rust-analyzer的构建时间,包括利用CI缓存、拆分CI任务、禁用增量编译和调试信息、减少依赖项、使用`cargo build -Z timings`进行性能分析,以及精心设计代码以避免泛型代码在crate边界上的过度实例化等策略。文章强调了构建时间对开发效率的影响,并建议定期优化构建时间,将大型Rust项目的CI时间控制在合理范围内,例如10分钟左右。
阅读更多
作者以自身使用NixOS和KDE软件的经历为引子,探讨了Linux桌面环境下开源软件协调的困境。他指出,Linux桌面缺乏统一的API标准,导致软件生态碎片化,如同一个“埃舍尔式的永动机”。这与十年前微软发布的语言服务器协议(LSP)形成对比,LSP尽管实现一般,但其存在本身解决了IDE功能的协调问题,推动了行业发展。作者认为,开源社区缺乏协调能力,错失了在LSP出现前制定统一IDE协议的机会。而Linux的成功,则得益于POSIX提供的预先定义的API标准,减少了协调难度。这篇文章引发了对开源社区协调机制及软件生态发展模式的思考,归类为科技。
阅读更多
本文介绍了提升代码效率的两个实用技巧:将if条件上移和for循环下移。将if条件上移到调用函数处,可以减少代码分支,简化控制流程,并提升代码可读性。将for循环下移到处理批量数据的地方,可以利用批量处理的优势,提升性能,并可能开启向量化操作。这两个技巧互相补充,可以有效提高代码效率,尤其在处理大量数据时效果显著。
阅读更多
作者在构建状态服务时,发现单事件处理(Scalar Select)模式效率低下。文章以一个LSP服务器为例,阐述了该模式如何导致处理延迟和资源浪费。作者提出了一种改进方案:批量处理事件流。通过`batch_stream`函数,将多个事件合并成批次处理,从而显著提高效率。这种方法在低负载下表现为单事件处理,但在高负载下能够有效降低处理开销,提升系统性能。
阅读更多
Zig 语言的编译时 (comptime) 功能强大,支持泛型、条件编译等,但其设计上刻意限制了某些功能,例如不允许动态代码生成、自定义语法扩展、运行时类型信息 (RTTI) 和 I/O 操作。文章深入探讨了这些限制背后的原因,以及 Zig 如何通过部分求值和类型特化等机制,在限制中实现高效且易于理解的元编程。通过一个自定义打印函数的例子,展示了 Zig 如何在不依赖 RTTI 的情况下实现类型安全的运行时反射。最终,文章赞扬了 Zig 在元编程上的独特优雅之处,它虽然功能不如其他语言强大,但在实际应用中却异常高效易用。
阅读更多
作者厌倦了传统调试器的繁琐,尤其是gdb和lldb在原生代码调试中的无力感。他发现IntelliJ IDEA的“Run to Cursor”和“Quick Evaluate Expression”功能组合,可以将调试器转变为强大的REPL环境。通过“Run to Cursor”将程序运行到光标所在行,再用“Quick Evaluate Expression”在当前栈帧中评估表达式,甚至可以输入新的代码并执行,实现了高效的代码探索和实验。这种方式摒弃了传统的单行命令交互,而是利用编辑器的二维文本界面,提供代码补全等特性,极大提升了调试效率。
阅读更多
本文介绍了作者使用极简API(无动态内存分配)编写Rust应用程序的案例研究。作者批评了RAII(资源获取即初始化)机制导致资源管理混乱,并提出了一种“硬核模式”:将程序分割成`std`二进制文件和`#![no_std] no_alloc`库,仅允许二进制文件直接向操作系统请求资源。文章以一个玩具光线追踪器为例,详细讲解了在“硬核模式”下如何处理像素缓冲区、并行化、内存分配器和场景解析等问题,最终实现了一个无需动态内存分配的光线追踪器。
阅读更多
本文探讨了编程中“不变式”的概念及其应用。作者从一个小例子——编写一个计算插入点的二分查找变体——出发,阐述了如何通过明确定义并维护不变式来编写正确的代码。 文中指出,不变式是一种在系统动态演变过程中始终保持的属性,它能够简化推理过程,避免因考虑多种执行路径而带来的复杂性。 作者还以Cargo、rust-analyzer和TigerBeetle等项目为例,展示了在大型系统中应用不变式带来的好处,例如提高代码可维护性和性能。最终,作者总结了不变式在小规模和大型编程中的重要作用,强调了其在编写正确且高效代码中的价值。
阅读更多
作者提出了一种新颖的思路,将Emacs的Magit版本控制界面(以文本文件为UI)应用于新兴的jj版本控制生态系统。文章指出,Magit的文本化UI具有高效性和可移植性,通过LSP协议,可以在多种编辑器中实现类似Magit的体验,避免重复开发。作者设想通过生成特定文本文件(如.jj/status.jj),并利用LSP的语义标记、折叠范围和跳转定义等功能,实现与Magit类似的版本控制操作,最终目标是创建一个跨平台、高效的jj版本控制用户界面。
阅读更多
io_uring 是一种全新的 Linux 内核接口,用于进行系统调用。与传统的同步、逐个提交系统调用的方式不同,io_uring 采用批量异步方式。应用程序通过将系统调用代码和参数写入无锁共享内存环形缓冲区来提交多个系统调用。内核读取并按自身节奏执行,结果异步写入第二个环形缓冲区供应用程序读取。
阅读更多
这篇文章探讨了Rust语法,作者认为人们对Rust语法的抱怨实际上是对其语义的反对。文章以一个读取文件的Rust函数为例,展示了其语法复杂性,并尝试用其他语言风格改写,以期找到更简洁的表达方式。作者进一步探讨了简化Rust语义的可能性,例如移除泛型、借用和错误处理机制,最终得到了一个更简洁但牺牲了性能和安全性的版本。
阅读更多
本文讨论了图灵完备性的概念,并指出在实践中,非图灵完备性并不像人们通常认为的那样重要。作者通过介绍有限状态机、图灵机和原始递归函数,证明了任何在图灵机上运行并在原始递归函数时间内终止的算法都可以用原始递归函数实现。文章还探讨了良好配置语言的特性,包括确定性、定义明确、纯洁性、安全性、沙盒化和简单性,并指出非图灵完备性本身并不能保证这些特性。
阅读更多
文章介绍了作者如何利用 Git 工作树来管理并发任务,而不是将其作为分支的替代品。作者创建了五个工作树,分别用于查看原始代码、编写代码、审查代码、运行模糊测试以及处理其他临时任务。这种方法提高了作者的编码效率,并能更好地管理并发任务。
阅读更多
文章介绍了一种测试并发数据结构的方法,通过模拟并控制线程的执行顺序,实现了对并发操作的精细化测试。作者首先解释了传统并发测试的不足,然后逐步推导了一种基于“管理线程”的测试方法,并用一个简单的计数器示例演示了如何使用该方法发现并最小化并发错误。文章最后还探讨了如何将该方法扩展到更复杂的场景,例如模拟弱内存模型和穷举所有可能的交错执行路径。
阅读更多