你最后那句"半夜再不会被内存泄漏叫醒"得稍微改改口径。borrow checker 拦的是内存不安全——use-after-free、悬垂指针、别名冲突——不是内存泄漏。safe Rust 里照样能泄漏:Rc 成环、故意 mem::forget、或者就有个值一直活到程序退出,这些它都不管,而且语言层面认为"泄漏是 safe 的"。简单说所以你换来的踏实,准确说是"不会被未定义行为吓醒",而不是"绝不漏内存",这俩差别挺大。
其实
顺着 ownership 这点往下说。Rust 真正稀缺的不是"强制你想清楚所有权",而是把这套约束塞进了类型系统、在编译期做成可检查的。简单说GC 语言(Go、Java)其实也要求你想清楚生命周期,只是把账挪到运行时去算,代价是停顿和吞吐。Rust 是把代价预付了:你写的时候难受,跑起来不付。这是 trade-off,不是免费升级。
其实
你提到数据竞争那块是 Rust 最硬的一张牌。Send/Sync 那套模型保证 safe Rust 里没有 data race,不是吹的。微软说过他们七成多的 CVE 是内存安全问题,Android 上了 Rust 之后这类漏洞直接砍掉一半多。这份"踏实"是有数据撑着的。
不过"不自由换踏实"也有边界。自引用结构、图、带缓存的惰性计算,borrow checker 就是会挡,硬刚逼你上 Rc/RefCell,到头来还是运行时借用,编译期保证打折。必要时 unsafe 该用用,它不代表失败,是把安全检查的责任从编译器转到你头上,把范围圈小了反而可控。
另外你那"头两个月天天较劲"的体感,一部分也是时代红利——2018 的 NLL(非词法生命周期)之后,借用检查器比原来聪明一大截,很多以前得 clone 才能过的代码现在直接过。现在入门比那会儿顺多了。
说回"拥有"本身,我觉得 Rust 最妙的不是让你更谨慎,而是逼你把"谁该负责收尾"从模糊直觉变成代码里白纸黑字的东西。其实这个习惯一旦建立,回头用别的语言写东西也会下意识先想清楚生命周期。
你后来有试过用 unsafe 或者 RefCell 去拆那些被 borrow checker 拦下来的地方吗?