Toasty,一个针对Rust的异步ORM,现在已经发布在crates.io上

7 阅读6分钟

本文首发 猩猩程序员 公众号

2026年4月3日

Toasty是一个为Rust编程语言设计的异步ORM,旨在优先考虑易用性。Toasty支持SQL和NoSQL数据库。目前,在NoSQL方面,只有DynamoDB得到了支持,但我希望在接下来的几个月中能增加更多的支持。

以下是如何使用Toasty的示例:

#[derive(Debug, toasty::Model)]
struct User {
    #[key]
    #[auto]
    id: u64,

    name: String,

    #[unique]
    email: String,

    #[has_many]
    todos: toasty::HasMany<Todo>,
}

#[derive(Debug, toasty::Model)]
struct Todo {
    #[key]
    #[auto]
    id: u64,

    #[index]
    user_id: u64,

    #[belongs_to(key = user_id, references = id)]
    user: toasty::BelongsTo<User>,

    title: String,
}

// 创建一个用户,ID没有指定,因为它会自动设置
toasty::create!(User {
    name: "John Doe",
    email: "john@example.com",
}).exec(&mut db).await?;

// 通过电子邮件查找用户
let user = User::get_by_email(&mut db, "john@example.com").await?;

要了解更多如何使用Toasty的内容,可以查看用户指南和API文档。

为什么选择ORM?

在2024年10月,我第一次宣布了Toasty,当时我也谈到过一些原因。简单来说,我认为Rust可以成为一个高度生产力的语言,适用于更高层次的应用程序,包括更传统的Web应用程序。虽然Rust可能永远无法达到JavaScript那样的快速原型开发速度,但它在其他方面提供了很多优势。Rust几乎适用于任何应用程序,无论是基础设施级别的高性能系统、CLI工具、嵌入到移动应用中,还是通过WASM在浏览器中运行。能够使用同一种语言、一套工具,不需要在不同语言之间跳跃,这本身就能提高生产力。

此外,自从最初的宣布以来,情况发生了很大变化(让我穿上防火服)。我相信AI将成为Rust采用的强大助力。Rust的一个最大障碍是入门门槛。即使没有“振奋人心的编程”,仅仅使用AI作为学习工具也是一个巨大的助力。我看到许多开发者通过使用AI来学习,同时也能快速交付代码,从而变得更加高效。而且,AI工具本身的速度并不快,这使得Rust的慢编译过程变得不那么成为阻碍。

说实话,秘密已经被揭开——许多开发者将让AI“掌舵”。Rust强大的类型系统和约束机制将在这方面发挥作用;语言能为AI工具设定的约束越多,最终的结果就越好。是的,肯定会有“脏活”,因此具有强大约定的高层库在利用Rust的类型系统时变得至关重要。强大的抽象可以帮助限制“脏活”的范围。

但所有这些只有在生态系统的支持下才能发挥作用,这意味着需要有高效的、适用于Web应用程序构建的高层库。ORM是其中的重要组成部分。

为什么花了这么久?

我在一年前就宣布了在开发Toasty的计划,实际上在那之前我就已经开始做了。最初的提交已经有超过两年了。从那时起,到现在经过了很长时间才完成。

如果你对比最初宣布的Toasty和现在的Toasty,会发现API有很大不同。在最初的宣布阶段,你们提供了很多宝贵的反馈,指出了使用schema文件的方式不好。我听取了意见,重新开始设计。我最初选择使用schema文件,是因为我认为用#[derive(Model)]来实现我想要的所有特性似乎不可行。然而,在深入研究后,我最终搞明白了!所以,感谢所有提供真诚反馈的朋友 :)

实际上,这个项目比我最初预期的要更具挑战性。我最初并没有一个非常清晰的愿景来构建这个项目。随着开发的推进,我逐渐发现了自己的方向,并且在学习更多的过程中,我不得不几次重写内部实现。

不仅仅是ORM

虽然我称其为ORM,但它实际上是一个“应用级查询引擎”。在Toasty中,应用程序模式和数据库模式是完全解耦的。当你使用Toasty时,你是直接针对应用程序模式编写Toasty“语句”的,这种模式更像是图形而非一组关系。然后,Toasty查询引擎会查看该语句,并根据目标数据库的能力将其转换为一组数据库操作。

当我开始开发Toasty时,我就知道我想尽可能支持NoSQL数据库。正如我在第一篇博客文章中提到的那样,目标并不是将数据库的能力抽象掉,而是尝试在不同类型的数据库中提供一个一致的API,使得无论是使用SQL还是NoSQL,开发者都能轻松使用。你仍然需要理解数据库的能力,适当地建模数据,并聪明地构建查询,但具体的API是一样的。

我最初以为支持SQL和NoSQL是件简单的事。Toasty的语句在针对SQL时应该几乎可以原样传递给数据库,而在针对NoSQL时,查询引擎将进行规划,决定是否以及如何执行查询。然而,我发现即使在SQL方面,我也需要比预期更多地依赖“查询引擎”部分。很多应用程序中有用的查询模式并不容易用SQL表达。例如,通过键更新一行并设定预条件,同时能够区分“未找到键”和“预条件失败”的情况。对于PostgreSQL而言,这需要写成一个CTE,并执行多个查询。因此,即便是在SQL这方面,查询引擎也发挥了很大的作用。

下一步是什么?

这只是一个早期版本。Toasty是一个大项目,现实世界中的使用还不多(但会有的)。非常感谢那些勇敢的灵魂,他们已经尝试了Toasty并提供了反馈,指出了不足之处。目前,Toasty的功能应该足够先进,可以开始进行基本的构建了。还有很多功能需要添加,但由于核心基础已经稳定,我对进展持乐观态度。事实上,过去几个月在新增功能方面取得了巨大的进展。

我预计我们将在0.x版本的发布过程中频繁发布破坏性更改,因此会有一定的波动。我也预料到会有一些粗糙的边缘和bug,随着更多人尝试使用它,但我会努力跟进!

所以,请尝试使用Toasty,告诉我你的想法!阅读文档,在GitHub讨论、Discord(#toasty频道)上分享你的反馈,或者在几周后和我一起在TokioConf现场聊聊!

本文首发 猩猩程序员 公众号