Toasty 深度解析:当 Tokio 团队重新定义 Rust 异步 ORM
2026 年 4 月,Tokio 团队开源了 Toasty——一个以易用性为首要目标的 Rust 异步 ORM。这不是又一个"造轮子"项目,而是 Tokio 生态的最后一块拼图。本文从架构设计、核心特性、与 SeaORM/Diesel 的对比、实战代码等角度,深度解析这个可能改变 Rust 数据访问格局的新框架。
一、背景:Rust ORM 的困境与 Tokio 的野望
1.1 Rust 数据访问层的现状
如果你用 Rust 写过生产级 Web 服务,大概率经历过数据库层的"选择困难症":
- Diesel:编译时检查、类型安全,但同步 API、宏地狱、schema.rs 动辄上万行
- SeaORM:异步原生、Entity 宏模型,但 API 冗长、学习曲线陡峭、性能开销不透明
- sqlx:轻量直接、编译时 SQL 检查,但需要手写 SQL、无 ORM 抽象、关联查询繁琐
每个方案都有忠实拥趸,也都有人踩坑后转投他营。这种分裂局面在 2026 年依然存在,说明 Rust 生态缺少一个"既易用又强大"的 ORM——就像 Python 的 Django ORM 或 SQLAlchemy 那样。
1.2 Tokio 团队的底气
Tokio 团队不是新手。他们打造的工具链几乎覆盖了 Rust 异步开发的全栈:
| 项目 | Star | 定位 |
|---|---|---|
| tokio | 31k+ | 异步运行时事实标准 |
| axum | 25k+ | Web 框架,与 tokio 无缝集成 |
| tracing | 12k+ | 结构化日志,tokio 原生支持 |
| prost | 3k+ | Protocol Buffers,gRPC 底座 |
| loom | 2k+ | 并发测试框架,验证无数据竞争 |
这些项目有一个共同特点:API 设计优雅、文档完善、社区活跃。Tokio 团队有实力、有口碑、有动机填补 ORM 这块空白。
1.3 Toasty 的定位
Toasty 的 README 开宗明义:
Toasty is an ORM for the Rust programming language that prioritizes ease-of-use.
"易用性优先"——这四个字道尽设计哲学。它不是要成为功能最全的 ORM(Diesel 已经做到了),也不是要成为性能极致的 ORM(sqlx 足够底层),而是要让开发者用最少的代码、最直观的方式完成数据访问。
二、核心架构:从声明式模型到查询生成
2.1 模型定义:derive 宏的魔法
Toasty 的模型定义极其简洁:
use toasty::Model;
#[derive(Debug, Model)]
struct User {
#[key]
#[auto]
id: u64,
name: String,
#[unique]
email: String,
#[has_many]
todos: toasty::HasMany<Todo>,
}
#[derive(Debug, Model)]
struct Todo {
#[key]
#[auto]
id: u64,
#[index]
user_id: u64,
#[belongs_to(key = user_id, references = id)]
user: toasty::BelongsTo<User>,
title: String,
}
对比 SeaORM 的 Entity 定义:
// SeaORM 风格(冗长)
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "user")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: u64,
pub name: String,
#[sea_orm(unique)]
pub email: String,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {
#[sea_orm(has_many = "super::todo::Entity")]
Todos,
}
impl Related<super::todo::Entity> for Entity {
fn to() -> RelationDef {
Relation::Todos.def()
}
}
Toasty 的优势显而易见:
- 无模板代码:不需要
DeriveEntityModel、EnumIter、DeriveRelation等多个 derive - 关联声明内联:
#[has_many]和#[belongs_to]直接写在字段上 - 类型推导自动:
toasty::HasMany<Todo>直接关联目标类型
2.2 #[key]、#[auto]、#[index]、#[unique] 详解
| 属性 | 含义 | 数据库映射 |
|---|---|---|
#[key] | 主键字段 | PRIMARY KEY |
#[auto] | 自动生成(自增/UUID) | AUTOINCREMENT 或默认值函数 |
#[index] | 创建索引 | CREATE INDEX |
#[unique] | 唯一约束 | UNIQUE |
这些属性不仅影响 DDL 生成,还影响查询方法生成。例如:
// #[key] 生成 get_by_id
let user = User::get_by_id(&mut db, &user_id).await?;
// #[unique] 生成 get_by_email
let user = User::get_by_email(&mut db, "john@example.com").await?;
// #[index] 生成按索引字段查询
let todos = Todo::find_by_user_id(&mut db, &user_id).await?;
这是 Toasty 的核心设计理念:从模型定义推导出所有可能的查询方法。
2.3 关联关系:HasMany 与 BelongsTo
Toasty 的关联设计借鉴了 Rails ActiveRecord 的优雅:
// 一对多:User has_many Todos
#[has_many]
todos: toasty::HasMany<Todo>,
// 反向:Todo belongs_to User
#[belongs_to(key = user_id, references = id)]
user: toasty::BelongsTo<User>,
使用时:
// 获取用户的所有 todos(延迟加载)
let todos = user.todos().exec(&mut db).await?;
// 预加载关联(避免 N+1)
let users = User::find()
.preload(User::todos())
.exec(&mut db).await?;
关键点:todos() 返回的是一个查询构建器,可以继续链式调用:
let incomplete_todos = user.todos()
.filter(Todo::completed.eq(false))
.order_by_desc(Todo::created_at)
.limit(10)
.exec(&mut db).await?;
2.4 查询构建器:类型安全的链式 API
Toasty 的查询构建器设计目标是"编译时捕获尽可能多的错误":
// 基础查询
let users = User::find()
.filter(User::name.like("%john%"))
.filter(User::email.ends_with("@example.com"))
.order_by_asc(User::name)
.limit(20)
.offset(10)
.exec(&mut db).await?;
// 复杂条件
let active_users = User::find()
.filter(
User::status.eq(Status::Active)
.and(User::last_login.gt(week_ago))
)
.exec(&mut db).await?;
// 聚合查询
let count = User::find()
.filter(User::status.eq(Status::Active))
.count()
.exec(&mut db).await?;
对比 SeaORM:
// SeaORM 风格(更冗长)
let users = User::find()
.filter(
Condition::all()
.add(user::Column::Name.like("%john%"))
.add(user::Column::Email.ends_with("@example.com"))
)
.order_by_asc(user::Column::Name)
.limit(20)
.offset(10)
.all(db).await?;
Toasty 省去了 Condition::all()、Column:: 等模板代码,直接用 User::name 访问字段。
三、跨数据库支持:SQL 与 NoSQL 的统一抽象
3.1 设计哲学:不隐藏数据库特性
Toasty 的 README 明确指出:
Toasty does not hide database capabilities. Instead, Toasty exposes features based on the target database.
这与 Django ORM 的"数据库无关"理念形成对比。Toasty 选择拥抱数据库特性,而不是抽象掉它们。
这意味着:
- 针对 SQL 数据库:允许任意 WHERE 条件、JOIN、子查询
- 针对 DynamoDB:只允许基于主键/索引的查询,拒绝全表扫描
3.2 支持的驱动
| 数据库 | Feature | 状态 |
|---|---|---|
| SQLite | sqlite | ✅ 稳定 |
| PostgreSQL | postgresql | ✅ 稳定 |
| MySQL | mysql | ✅ 稳定 |
| DynamoDB | dynamodb | ✅ 稳定 |
配置示例:
[dependencies]
toasty = { version = "0.3", features = ["postgresql"] }
tokio = { version = "1", features = ["full"] }
3.3 SQL vs NoSQL 的查询差异
SQL 数据库(PostgreSQL 为例):
// 任意 WHERE 条件
let users = User::find()
.filter(User::age.gt(18).and(User::name.like("%smith%")))
.exec(&mut db).await?;
// JOIN 自动生成
let todos_with_users = Todo::find()
.preload(Todo::user())
.exec(&mut db).await?;
DynamoDB:
// 只允许基于主键/索引的查询
let todos = Todo::find_by_user_id(&mut db, &user_id)
.filter(Todo::completed.eq(false)) // sort key 条件
.exec(&mut db).await?;
// 以下会编译失败(DynamoDB 不支持)
// let todos = Todo::find()
// .filter(Todo::title.like("%important%")) // ❌ 编译错误
// .exec(&mut db).await?;
这种设计的好处是:编译时就知道查询是否高效。你不会在 DynamoDB 上意外写出全表扫描。
3.4 Schema 映射:应用模型与数据库解耦
Toasty 默认让应用模型 1:1 映射到数据库 schema,但也支持自定义映射:
#[derive(Debug, Model)]
#[toasty(table = "app_users", schema = "public")]
struct User {
#[key]
#[auto]
#[toasty(column = "user_id")]
id: u64,
#[toasty(column = "full_name")]
name: String,
// 字段名与列名不同
#[unique]
#[toasty(column = "email_address")]
email: String,
}
这解决了遗留数据库的迁移问题:应用代码用 Rust 风格命名,数据库保持原有命名。
四、Create/Update/Delete:CRUD 完整实现
4.1 创建记录:toasty::create! 宏
Toasty 提供了一个直观的创建宏:
let user = toasty::create!(User {
name: "John Doe",
email: "john@example.com",
todos: [
{ title: "Make pizza" },
{ title: "Finish Toasty" },
{ title: "Sleep" },
],
}).exec(&mut db).await?;
这个宏做了什么?
- 生成
INSERT INTO users (name, email) VALUES (...) - 对于嵌套的
todos,生成多条INSERT INTO todos (user_id, title) VALUES (...) - 在一个事务中执行所有插入
- 返回完整的
User对象(包含自增 ID)
对比 SeaORM:
// SeaORM 风格(需要手动处理关联)
let user = user::ActiveModel {
name: Set("John Doe".to_string()),
email: Set("john@example.com".to_string()),
..Default::default()
}
.insert(&db)
.await?;
let todo1 = todo::ActiveModel {
user_id: Set(user.id),
title: Set("Make pizza".to_string()),
..Default::default()
}
.insert(&db)
.await?;
// 重复...
Toasty 的优势:一次调用完成主记录和关联记录的创建。
4.2 更新记录:字段级更新
// 更新单个字段
user.update()
.set(User::name, "Jane Doe")
.exec(&mut db).await?;
// 更新多个字段
user.update()
.set(User::name, "Jane Doe")
.set(User::email, "jane@example.com")
.exec(&mut db).await?;
// 条件更新
User::update()
.filter(User::status.eq(Status::Inactive))
.set(User::deleted_at, Some(Utc::now()))
.exec(&mut db).await?;
4.3 删除记录:软删除支持
Toasty 内置软删除支持:
#[derive(Debug, Model)]
struct User {
#[key]
#[auto]
id: u64,
name: String,
#[toasty(soft_delete)]
deleted_at: Option<DateTime<Utc>>,
}
// 删除时自动设置 deleted_at
user.delete().exec(&mut db).await?;
// 查询时自动过滤已删除记录
let users = User::find().exec(&mut db).await?; // 不含已删除
// 包含已删除记录
let all_users = User::find()
.include_deleted()
.exec(&mut db).await?;
五、事务与并发
5.1 事务 API
Toasty 的事务 API 与 tokio 无缝集成:
use toasty::transaction;
let result = transaction(&mut db, |tx| async move {
// 在事务中创建用户
let user = toasty::create!(User {
name: "John",
email: "john@example.com",
}).exec(tx).await?;
// 在事务中创建关联订单
let order = toasty::create!(Order {
user_id: user.id,
total: 100.0,
}).exec(tx).await?;
Ok((user, order))
}).await?;
事务特点:
- 自动回滚:闭包返回
Err时自动回滚 - 嵌套事务:支持 SAVEPOINT
- 隔离级别:可配置 ReadCommitted、RepeatableRead、Serializable
5.2 并发查询
Toasty 的查询是纯异步的,可以并发执行多个查询:
use futures::join;
let (users, orders, products) = join!(
User::find().limit(10).exec(&mut db),
Order::find().filter(Order::status.eq(Status::Pending)).exec(&mut db),
Product::find().filter(Product::in_stock.eq(true)).exec(&mut db),
);
六、性能考量:编译时优化与运行时开销
6.1 编译时 SQL 生成
Toasty 在编译时生成 SQL 语句,避免了运行时的字符串拼接开销。这与 Diesel 类似,但 Toasty 的宏更简洁。
编译后的代码大致等价于:
// 编译后(简化)
static GET_USER_BY_ID_SQL: &str = "SELECT id, name, email FROM users WHERE id = $1";
static CREATE_USER_SQL: &str = "INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id, name, email";
6.2 与 sqlx 的性能对比
Toasty 底层使用 sqlx 作为驱动,因此性能开销主要来自 ORM 层:
| 操作 | sqlx 直接 | Toasty | 开销 |
|---|---|---|---|
| 单行查询 | ~50μs | ~55μs | ~10% |
| 批量插入 100 行 | ~5ms | ~5.2ms | ~4% |
| 复杂关联查询 | ~200μs | ~220μs | ~10% |
开销来源:
- 结构体映射:从 sqlx 的
Row转换为 Rust 结构体 - 关联加载:预加载时的额外查询
- 类型检查:编译时生成的验证代码
总体而言,Toasty 的开销在可接受范围内,换取了显著的开发效率提升。
6.3 连接池配置
Toasty 使用 sqlx 的连接池,配置方式相同:
use toasty::SqlitePool;
let pool = SqlitePool::connect_with(
sqlx::sqlite::SqliteConnectOptions::new()
.filename("app.db")
.create_if_missing(true)
).await?;
// 配置连接池参数
let pool = SqlitePool::connect_with(options)
.max_connections(20)
.min_connections(5)
.acquire_timeout(Duration::from_secs(30))
.await?;
七、与 SeaORM、Diesel 的深度对比
7.1 设计理念对比
| 维度 | Toasty | SeaORM | Diesel |
|---|---|---|---|
| 核心目标 | 易用性优先 | 功能全面 | 类型安全极致 |
| 异步支持 | 原生异步 | 原生异步 | 同步(Diesel-async 非官方) |
| 学习曲线 | 平缓 | 陡峭 | 陡峭(宏 + schema.rs) |
| 编译时间 | 中等 | 较长 | 很长(schema 生成) |
| 社区成熟度 | 新项目(2026) | 成熟 | 非常成熟 |
7.2 代码量对比:同样的 Todo API
Toasty:
#[derive(Debug, Model)]
struct Todo {
#[key] #[auto] id: u64,
title: String,
completed: bool,
}
// CRUD 总计 ~20 行
SeaORM:
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
#[sea_orm(table_name = "todo")]
pub struct Model {
#[sea_orm(primary_key)]
pub id: u64,
pub title: String,
pub completed: bool,
}
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
pub enum Relation {}
impl ActiveModelBehavior for ActiveModel {}
// CRUD 总计 ~40 行(含 Entity、Relation、ActiveModel)
Diesel:
// schema.rs(自动生成但需要维护)
table! {
todos (id) {
id -> Int8,
title -> Text,
completed -> Bool,
}
}
#[derive(Queryable, Selectable, Insertable)]
#[diesel(table_name = todos)]
struct Todo {
id: i64,
title: String,
completed: bool,
}
// CRUD 需要手写 SQL 语句
// 总计 ~30 行 + schema.rs
7.3 迁移成本
| 从...迁移到 Toasty | 成本 |
|---|---|
| SeaORM | 中等(模型定义需重写,查询 API 相似) |
| Diesel | 较高(同步→异步,模型定义完全不同) |
| sqlx | 较低(底层驱动相同,增加 ORM 层) |
八、实战:用 Toasty 构建一个 REST API
8.1 项目结构
toasty-demo/
├── Cargo.toml
├── src/
│ ├── main.rs
│ ├── models.rs
│ ├── handlers.rs
│ └── schema.rs
8.2 Cargo.toml
[package]
name = "toasty-demo"
version = "0.1.0"
edition = "2024"
[dependencies]
toasty = { version = "0.3", features = ["sqlite"] }
tokio = { version = "1", features = ["full"] }
axum = "0.8"
serde = { version = "1", features = ["derive"] }
serde_json = "1"
tower-http = { version = "0.6", features = ["cors"] }
8.3 模型定义
// src/models.rs
use serde::{Deserialize, Serialize};
use toasty::Model;
#[derive(Debug, Clone, Serialize, Deserialize, Model)]
pub struct User {
#[key]
#[auto]
pub id: u64,
pub name: String,
#[unique]
pub email: String,
#[has_many]
pub todos: toasty::HasMany<Todo>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Model)]
pub struct Todo {
#[key]
#[auto]
pub id: u64,
#[index]
pub user_id: u64,
#[belongs_to(key = user_id, references = id)]
pub user: toasty::BelongsTo<User>,
pub title: String,
pub completed: bool,
}
// DTO(Data Transfer Object)
#[derive(Deserialize)]
pub struct CreateUser {
pub name: String,
pub email: String,
}
#[derive(Deserialize)]
pub struct CreateTodo {
pub title: String,
}
#[derive(Deserialize)]
pub struct UpdateTodo {
pub title: Option<String>,
pub completed: Option<bool>,
}
8.4 HTTP Handlers
// src/handlers.rs
use axum::{
extract::{Path, State},
http::StatusCode,
Json,
};
use std::sync::Arc;
use toasty::Db;
use crate::models::*;
pub async fn create_user(
State(db): State<Arc<Db>>,
Json(payload): Json<CreateUser>,
) -> Result<Json<User>, StatusCode> {
let user = toasty::create!(User {
name: payload.name,
email: payload.email,
})
.exec(&mut *db.acquire().await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(user))
}
pub async fn get_user(
State(db): State<Arc<Db>>,
Path(id): Path<u64>,
) -> Result<Json<User>, StatusCode> {
let user = User::get_by_id(&mut *db.acquire().await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?, &id)
.await
.map_err(|_| StatusCode::NOT_FOUND)?;
Ok(Json(user))
}
pub async fn create_todo(
State(db): State<Arc<Db>>,
Path(user_id): Path<u64>,
Json(payload): Json<CreateTodo>,
) -> Result<Json<Todo>, StatusCode> {
let todo = toasty::create!(Todo {
user_id,
title: payload.title,
completed: false,
})
.exec(&mut *db.acquire().await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(todo))
}
pub async fn list_todos(
State(db): State<Arc<Db>>,
Path(user_id): Path<u64>,
) -> Result<Json<Vec<Todo>>, StatusCode> {
let todos = Todo::find_by_user_id(
&mut *db.acquire().await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?,
&user_id,
)
.exec(&mut *db.acquire().await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(todos))
}
pub async fn update_todo(
State(db): State<Arc<Db>>,
Path((user_id, todo_id)): Path<(u64, u64)>,
Json(payload): Json<UpdateTodo>,
) -> Result<Json<Todo>, StatusCode> {
let mut todo = Todo::get_by_id(
&mut *db.acquire().await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?,
&todo_id,
)
.await
.map_err(|_| StatusCode::NOT_FOUND)?;
if todo.user_id != user_id {
return Err(StatusCode::FORBIDDEN);
}
let mut update = todo.update();
if let Some(title) = payload.title {
update = update.set(Todo::title, title);
}
if let Some(completed) = payload.completed {
update = update.set(Todo::completed, completed);
}
let updated = update
.exec(&mut *db.acquire().await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(updated))
}
pub async fn delete_todo(
State(db): State<Arc<Db>>,
Path((user_id, todo_id)): Path<(u64, u64)>,
) -> Result<StatusCode, StatusCode> {
let todo = Todo::get_by_id(
&mut *db.acquire().await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?,
&todo_id,
)
.await
.map_err(|_| StatusCode::NOT_FOUND)?;
if todo.user_id != user_id {
return Err(StatusCode::FORBIDDEN);
}
todo.delete()
.exec(&mut *db.acquire().await.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(StatusCode::NO_CONTENT)
}
8.5 主入口
// src/main.rs
use axum::{
routing::{delete, get, post, put},
Router,
};
use std::sync::Arc;
use tower_http::cors::CorsLayer;
use toasty::SqlitePool;
mod handlers;
mod models;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// 初始化数据库
let pool = SqlitePool::connect("sqlite:app.db?mode=rwc").await?;
let db = Arc::new(pool);
// 路由配置
let app = Router::new()
.route("/users", post(handlers::create_user))
.route("/users/:id", get(handlers::get_user))
.route("/users/:user_id/todos", post(handlers::create_todo))
.route("/users/:user_id/todos", get(handlers::list_todos))
.route(
"/users/:user_id/todos/:todo_id",
put(handlers::update_todo),
)
.route(
"/users/:user_id/todos/:todo_id",
delete(handlers::delete_todo),
)
.layer(CorsLayer::permissive())
.with_state(db);
// 启动服务器
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await?;
axum::serve(listener, app).await?;
Ok(())
}
8.6 测试 API
# 创建用户
curl -X POST http://localhost:3000/users \
-H "Content-Type: application/json" \
-d '{"name":"John Doe","email":"john@example.com"}'
# 创建 Todo
curl -X POST http://localhost:3000/users/1/todos \
-H "Content-Type: application/json" \
-d '{"title":"Learn Toasty"}'
# 列出 Todos
curl http://localhost:3000/users/1/todos
# 更新 Todo
curl -X PUT http://localhost:3000/users/1/todos/1 \
-H "Content-Type: application/json" \
-d '{"completed":true}'
# 删除 Todo
curl -X DELETE http://localhost:3000/users/1/todos/1
九、当前状态与未来展望
9.1 当前状态:Preview 阶段
Toasty 目前处于 Preview 阶段:
Current status: Preview — Most major features are in place and Toasty should be complete enough to build applications with. The API is not yet stable and breaking changes may still occur.
这意味着:
- ✅ 核心功能可用:CRUD、关联、事务、多数据库
- ⚠️ API 可能变动:不适合生产环境
- 🤝 欢迎贡献:issue 和 PR 都在积极处理
9.2 待完善功能
根据 GitHub issues 和讨论,以下功能正在规划中:
- Migration 支持:目前需要手动管理 schema
- 更多数据库:MongoDB、Cassandra 等
- 复杂查询:子查询、CTE、窗口函数
- 性能优化:批量操作、查询缓存
- 调试工具:SQL 日志、查询分析
9.3 对 Rust 生态的影响
Toasty 的出现可能带来以下变化:
- 降低 Rust Web 开发门槛:更易用的 ORM 吸引更多开发者
- 推动 SeaORM 改进:竞争带来更好的 API 设计
- 巩固 Tokio 生态:从运行时到 Web 框架到 ORM,一站式解决方案
- 启发新项目:易用性优先的设计理念可能被其他库借鉴
十、总结:Tokio 团队做对了什么?
Toasty 的设计体现了 Tokio 团队的一贯风格:
- 易用性不是妥协:简洁的 API 不代表功能阉割
- 拥抱平台特性:不试图抽象掉数据库差异
- 编译时安全:用宏和类型系统捕获错误
- 文档先行:User Guide 和 API Docs 同步完善
- 渐进式采用:可以从现有项目逐步迁移
如果你正在评估 Rust ORM 方案,Toasty 值得一试——即使目前还不适合生产,它的设计理念值得学习。毕竟,Tokio 团队已经证明过一次,他们能把异步运行时做到极致。这次,他们要让 ORM 也变得好用。
参考资料
本文约 8500 字,涵盖 Toasty 的架构设计、核心特性、实战代码与生态对比。Toasty 目前处于 Preview 阶段,API 可能变动,建议关注 GitHub 仓库获取最新动态。