一、一个让我陷入沉思的仓库

说真的,我刚看到这个仓库的时候,第一反应是:“嗯?是不是有人在开玩笑?”

Rust编译成Go?这听起来就像——把特斯拉的电动引擎装进一辆经典马自达的车身里。有点跨界,有点意外。

它的名字叫Lisette,作者是Iván Ovejero。从语言本身到命名方式,都透着一股“不守常规”的气质。

短短三个月,在GitHub上攒下1.3k stars,编译器用Rust写(94.3%),生成的目标代码是Go(5.7%)。

作为一个写Go有些年头、也在Rust门口徘徊过的程序员,我第一反应其实是某种“技术上的困惑”——谁会需要这样的东西?为什么不让Rust直接编译成二进制,或者让Go“做得更像Rust”呢?

带着好奇,我把自己代入了两个场景,然后豁然开朗。

二、Go的“痛点”与Rust的“门槛”

2.1 Go让你“安心”,也让你“束手束脚”

Go的设计哲学很简单——简单、直接、生产力高。你甚至不用花太多心思去设计“完美的抽象”,写出来就能跑,并发模型优雅,编译速度快得感人。

但这种“务实”是有代价的。

我举个最典型的例子——空指针。Go里到处是nil。你明明写了个看起来没问题的代码,到了运行时“啪”一下panic: nil pointer dereference。Go在语言层面不帮你自动避免这个问题,它把这个责任完全交给了开发者——你,要自己检查。

在这里插入图片描述

再比如错误处理if err != nil写多了,真的会产生“重复性疲劳”。而且Go的错误只是普通的接口值,你很容易忽略它、忘记传递它,编译器基本不拦你。

还有零值。在Go里,声明一个变量但没初始化,它自动获得一个“零值”——int是0,string是空,指针是nil。在有些场景这很省事,但在另一些场景,零值可能不是一个合法的状态,你就需要写额外的逻辑来区分“真的零”和“未初始化”。

Lisette的README里把这些总结为**“Go issues Lisette prevents”**,并且特意给出了safety.md这个文档。

说句公道话:Go的设计者们不是不知道这些问题。零值和nil在某些情况下确实简化了代码,但它的确把“防御性编程”的负担转移给了开发者。 当代码规模大到一定程度,这些看似微小的问题会累积成巨大的维护成本。

2.2 Rust让你“安全”,也让你“想撞墙”

Rust用所有权、借用、生命周期这套体系,在编译期就解决了内存安全和并发安全的问题。你只要能让代码通过编译,基本不用担心空指针、悬垂指针、数据竞争。

问题在于——要让代码通过编译,真的很不容易。

我刚学Rust那会儿,被借用检查器折磨得怀疑人生。一个很简单的逻辑,我脑子里已经清清楚楚了,但编译器就是不肯放行。你得学习一堆新概念:所有权、借用、生命周期、BoxRcRefCell……每加一个新功能,都可能触发编译器的“灵魂拷问”。

Lisette的作者在“关于”页里引用了一句话,我觉得非常贴切:“Rust是一股必要的力量,但它需要您学习一套复杂的规则,并在代码中明确标注。”

用我自己的话说就是——Rust像一个极其严厉但无比可靠的老师,你能学到真东西,但过程中会无数次怀疑自己的智商。你付出的学习成本和心智负担是真实的。

三、Lisette的第三条路:用“Rust的精神”写出“Go的代码”

Lisette给出了一个巧妙又务实的第三条路:

让开发者用近似Rust的语法和心智模型来写代码,然后编译器把这些代码翻译成普通Go程序员能看懂、Go工具链能直接使用的Go代码。

3.1 语言特性:借鉴Rust,但绕过所有权

Lisette从Rust那里借鉴了它“安全且表达力强”的部分,但巧妙地绕开了所有权系统。

你可以用枚举(enum)模式匹配。这些在Rust里是非常习惯的用法,但在Go里,你只能用接口加类型断言来模拟,或者写一堆if-else。

在这里插入图片描述

Lisette直接用Go的通道和goroutine,但加上了更好的语法包装。你可以写task { ... }来启动一个并发任务,这在语感上比go func(){...}()要自然一些,而且和通道、select等组合使用时,代码更紧凑。

在这里插入图片描述

重要的是,Lisette是表达式导向、不可变优先的。在Go里,你下意识地会用:=去改变变量。但在Lisette里,默认倾向是创建新的值,这跟Rust的思路相似,有助于减少副作用。

在这里插入图片描述

它会生成什么?生成可读的Go代码。这一点很关键。

Lisette的作者在FAQ里明确写道,不会为了让代码更“聪明”而牺牲可读性。生成的Go代码要像人写的一样清晰。这意味着,你随时可以“降级”到Go层面去理解和调试。

3.2 与Go的互操作性:你不是在孤岛上

Lisette没有试图创造一个封闭的“完美世界”。它提供了与Go生态系统的互操作能力。

import "go:os"
import "go:fmt"

你可以直接在Lisette代码里导入Go的标准库或第三方包。Lisette会生成相应的Go代码,调用那些包。

同样,你也可以通过bindgen机制,把Lisette编译成Go包,供其他Go代码使用。

这意味着你可以渐进式地采用Lisette——只在新模块里用它,或者只把“特别需要表达力”的那部分逻辑用它来写,剩余的庞大系统保持不变。这不是“非此即彼”的选择,而是“既能……又能……”的融合。

四、最让我觉得妙的设计——管道运算符

Lisette里有一个让我眼前一亮的东西——管道运算符|>

let slug = "  Hello World  "
  |> strings.TrimSpace
  |> strings.ToLower
  |> strings.ReplaceAll(" ", "-")  // 结果是 "hello-world"

Go里要实现这个,你得写三步,还要一个临时变量:

tmp := strings.TrimSpace("  Hello World  ")
tmp = strings.ToLower(tmp)
slug := strings.ReplaceAll(tmp, " ", "-")

或者写一个长长的嵌套函数调用,从右往左读:

slug := strings.ReplaceAll(strings.ToLower(strings.TrimSpace("  Hello World  ")), " ", "-")

管道运算符让数据从左到右流动,每一步做什么一目了然。这在处理数据转换链时特别清晰,而且和Lisette的“表达式导向”风格完美契合。

据我所知,这个操作符在Elm、F#等函数式语言里很常见,Lisette直接把它带了进来。

五、对Go和Rust的再思考

Lisette的出现,让我对Go和Rust的关系有了新的理解。

以前我总觉得它们是对手——我该学哪个?我该用哪个?我的下一个项目用Go还是Rust?它们总是被放在天平的两端。

但现在我觉得,它们更像是两个坐标轴

Go解决了“我们怎么高效地协作、怎么让程序简单地跑起来”的问题。它的核心关注点是团队生产力和工程化。

Rust解决了“我们怎么能绝对安全地操控机器、怎么能不让任何人犯错”的问题。它的核心关注点是系统级的安全和控制。

这两个问题都很难,需要的技术取舍完全不同。所以它们在语言设计上走向了两个方向。

而Lisette试图做的是——用Rust的安全理念和表达力去写代码,但用Go的运行模型和工具链去交付软件。

它是一个“翻译层”,让你享受Rust的“精神福利”,但不用支付所有的“心智税”。至于所有权、生命周期这些最“劝退”的部分,Lisette没有照搬,而是通过代码生成、运行时检查等方式,在Go的现有模型上尽量保证安全。

一位社区评论员在Lisette的讨论区里写道:“Lisette不是在和Go竞争,它是在帮助Go做得更好。”

我觉得这个评价很准确。

六、Lisette适合谁?

根据我的理解,Lisette的目标用户可能是:

1. 被Go的nil和错误处理折磨的开发者

你爱Go的生产力,但受够了nil panic和if err != nil的重复劳动。你希望有更好的类型系统和错误传播机制,但不想抛弃Go的生态。

2. 被Rust的学习曲线劝退的开发者

你被Rust的安全承诺吸引,但无法说服团队(或自己)投入数月去掌握所有权和生命周期。你希望有一条“渐进式”的道路。

3. 对编程语言设计感兴趣的“语言极客”

Lisette本身就是一个很有趣的“混血”实验:一种语言编译到另一种语言,借鉴不同语言的特性,但又要保持生成代码的可读性和互操作性。这在编译器实现上有很多挑战。

4. 需要高表达力但最终要部署在Go环境的人

你要写一些复杂的逻辑转换、DSL处理、策略编排……用Go写起来很啰嗦,但你又必须和Go生态交互。Lisette可以作为“高级语言”前端,Go作为“汇编层”后端。

七、一些个人看法

说实话,Lisette目前还在早期(v0.3.0),很多特性还在开发中(比如与Go的完全互操作、标准库的完善度)。我不会建议把它用在明天的生产环境里。

但我挺欣赏它的思路。

Go和Rust,都带着各自鲜明的设计哲学,却也是各自“偏见”的产物。Lisette像一个“翻译官”,试图在两者之间搭一座桥。这座桥目前还有点窄,但方向我看不错。

我自己有个开源小工具,一直想重构,但一直犹豫用Go还是Rust——用Go,有些地方写起来很别扭;用Rust,担心后续接手的人维护不了。Lisette让我看到了一种可能:用接近Rust的表达力写逻辑,编译成Go,享受Go的生态和部署优势。如果它的互操作特性和稳定性成熟了,我会认真考虑。

语言之战还会继续。但像Lisette这样的项目提醒我们:与其争辩哪个锤子更好,不如想想怎么打出一颗更漂亮的钉子。

毕竟,程序员的最终目标,从来不是“精通某门语言”,而是“解决问题”。

Lisette解决的,正是“如何在两种伟大的语言之间,开辟一条更舒适的道路”这个问题。无论它最终能走多远,这种探索本身就很有价值。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐