rust在ai开发中的优势
我最近一直在想rust的特性,这种强编译时验证的语言可以把大部分潜在问题都拦截在编译期,意味着哪怕不写测试,它只要写出来天然就是一个自带测试的语言,这种高度可验证的特性让其非常适合 ai 时代。甚至说,如果我能通过一套良好的设计,把业务逻辑直接包裹在它的类型验证里面,在一定程度上甚至节省了写测试的功夫。
当然我不觉得它能代替测试,但至少作为约束可以限制ai的胡乱发挥。其次是它直接把class砍了,强迫你只能用组合去实现业务,也连带着把一些潜在的耦合危险消除了,从结构上就保证了边界必须清晰。而ai时代最需要的就是清晰,做不到这一点agent在编码时就会飘,而为了防止他飘,我必须用大量的「可验证」内容去保证他的正确性。
// C#
User user = GetUser(); // 可能返回 null
Console.WriteLine(user.Name); // 编译过。运行时如果 user 是 null → NullReferenceException
// Rust
let user: Option<User> = get_user();
println!("{}", user.name); // ❌ 编译直接失败:Option<User> 没有 .name 这个字段
而像上述这种编译期前置的特性,也迫使编写者必须在编译期就去处置潜在的不存在情况,否则会直接过不了编译,因而从根源上就堵住了一些编译上很完美,但运行时出错的边界突破问题。ai编码最怕的就是代码看上去没问题,但运行时才炸掉的情况——而此种情况下,找bug会浪费大量的时间,这也是开发时最棘手的问题。
其次则是多并发时的竞争问题,所有权这个rust最独特的特性,可以将这种运行时才会出现的问题,直接在编译期暴露出来。比如对于单一依赖的变量,完全可以将所有权(或使用权)固定(或临时固定)在使用者身上,直到他使用完再释放,这期间其他并发竞争者会直接无法使用,从编译上就直接解决争抢问题。当然,这一套对天然就是共享的变量无效(比如计数器),那这个时候,rust会迫使你用Mutex去明确处理这个问题,将无法在编译时检测的问题强迫显示,而不像其他语言一样自觉的锁去维护。
// 共享计数器,下面的写法会出错
let mut counter = 0;
let handle = thread::spawn(|| {
counter += 1; // 新线程想改 counter
});
counter += 1; // 主线程也想改 counter
use std::sync::{Arc, Mutex};
// Arc:多线程共享所有权;Mutex:同一时刻只放一个线程进去改
// 被迫显式处理此种共享情况
let counter = Arc::new(Mutex::new(0));
let c = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = c.lock().unwrap(); // 想碰里面的值?先拿锁。拿不到就等着
*num += 1;
}); // num 离开作用域 → 锁自动释放
换言之,在rust的情况下,竞争问题要么用所有权保证单一时刻只有一个使用者,要么就是用显式处理将边界明确包裹在编译好的处理范围内。
我始终觉得,要保证 AI 的质量,可验证性非常重要。测试只是这种可验证性的一个体现之一。但是从语言层面上怎么去保证可验证性呢?那就是像 Rust 的设计哲学一样,把一切都显式化,让它明确的在编译时就暴露各种各样问题出来,没写好就直接不过编译,把该约束的都约束完全,这种东西从来都是人写起来会很痛苦的玩意,可 AI 根本就不在意这些,就无论怎么看都非常合适。
编译层面上保证一切都显式化的严格约束,就像 Rust 这样。而另一条同样的必不可少的方面,就是在业务逻辑的层面上,用测试,尤其是强覆盖的测试去保证。而一套设计严格的语言,可以让编写测试的时候更加方便。比如说单测、集成测试。如果这个语言从编译上就把你各种各样的混同,乃至不可测试的写法给极大程度遏制了——或者应该这样讲,rust本身的设计使得一套指定良好的结构成为了预设选择,尽管烂代码还是无法靠语言保证,但只要遵循基本的原则,其基底质量就会是较高的。
一段代码是否可测试,严格意义上来讲是结构所保证的,而语言只是工具,一个规定更严格的语言就是好用的工具,我在这个层面上赞同rust。但没说规定不严格的语言就不能用结构去保证。像 TS 啊、 Python 呢,也能通过纯函数的形式去保证测试。但是对于 AI 来讲,它会有一些编译上的问题,以及要编写更多运行时才会暴露出来的问题。对 Rust 来讲,它可以在编译期间就直接解决。因为运行时的问题麻烦点在于,你不知道会遇到什么,Rust 它那种迫使你去覆盖边界的兜底强制要求,就会把很大一部分运行时才暴露的问题,至少收住一个尾,不至于灾难性崩塌。而且其实这个角度上去考虑的话,还涉及到一个 loop 的问题。尤其是那种自动化运行的 AI,如果你不给它一个明确的错误导向,它根本就没办法去 debug,这是很难的。它不像人类一样能去察觉到某些味道。所以说,一个显式的漂亮的错误处理,对于 AI 的 debug 来说非常重要。