编程 Toasty 深度解析:当 Tokio 团队重新定义 Rust 异步 ORM

2026-04-12 08:25:09 +0800 CST views 4

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定位
tokio31k+异步运行时事实标准
axum25k+Web 框架,与 tokio 无缝集成
tracing12k+结构化日志,tokio 原生支持
prost3k+Protocol Buffers,gRPC 底座
loom2k+并发测试框架,验证无数据竞争

这些项目有一个共同特点: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 的优势显而易见:

  1. 无模板代码:不需要 DeriveEntityModelEnumIterDeriveRelation 等多个 derive
  2. 关联声明内联#[has_many]#[belongs_to] 直接写在字段上
  3. 类型推导自动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 关联关系:HasManyBelongsTo

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状态
SQLitesqlite✅ 稳定
PostgreSQLpostgresql✅ 稳定
MySQLmysql✅ 稳定
DynamoDBdynamodb✅ 稳定

配置示例:

[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?;

这个宏做了什么?

  1. 生成 INSERT INTO users (name, email) VALUES (...)
  2. 对于嵌套的 todos,生成多条 INSERT INTO todos (user_id, title) VALUES (...)
  3. 在一个事务中执行所有插入
  4. 返回完整的 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%

开销来源:

  1. 结构体映射:从 sqlx 的 Row 转换为 Rust 结构体
  2. 关联加载:预加载时的额外查询
  3. 类型检查:编译时生成的验证代码

总体而言,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 设计理念对比

维度ToastySeaORMDiesel
核心目标易用性优先功能全面类型安全极致
异步支持原生异步原生异步同步(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 和讨论,以下功能正在规划中:

  1. Migration 支持:目前需要手动管理 schema
  2. 更多数据库:MongoDB、Cassandra 等
  3. 复杂查询:子查询、CTE、窗口函数
  4. 性能优化:批量操作、查询缓存
  5. 调试工具:SQL 日志、查询分析

9.3 对 Rust 生态的影响

Toasty 的出现可能带来以下变化:

  1. 降低 Rust Web 开发门槛:更易用的 ORM 吸引更多开发者
  2. 推动 SeaORM 改进:竞争带来更好的 API 设计
  3. 巩固 Tokio 生态:从运行时到 Web 框架到 ORM,一站式解决方案
  4. 启发新项目:易用性优先的设计理念可能被其他库借鉴

十、总结:Tokio 团队做对了什么?

Toasty 的设计体现了 Tokio 团队的一贯风格:

  1. 易用性不是妥协:简洁的 API 不代表功能阉割
  2. 拥抱平台特性:不试图抽象掉数据库差异
  3. 编译时安全:用宏和类型系统捕获错误
  4. 文档先行:User Guide 和 API Docs 同步完善
  5. 渐进式采用:可以从现有项目逐步迁移

如果你正在评估 Rust ORM 方案,Toasty 值得一试——即使目前还不适合生产,它的设计理念值得学习。毕竟,Tokio 团队已经证明过一次,他们能把异步运行时做到极致。这次,他们要让 ORM 也变得好用。


参考资料


本文约 8500 字,涵盖 Toasty 的架构设计、核心特性、实战代码与生态对比。Toasty 目前处于 Preview 阶段,API 可能变动,建议关注 GitHub 仓库获取最新动态。

复制全文 生成海报 Rust ORM Tokio 异步 数据库

推荐文章

API 管理系统售卖系统
2024-11-19 08:54:18 +0800 CST
Vue 3 中的 Fragments 是什么?
2024-11-17 17:05:46 +0800 CST
Linux查看系统配置常用命令
2024-11-17 18:20:42 +0800 CST
使用 Nginx 获取客户端真实 IP
2024-11-18 14:51:58 +0800 CST
Vue3中的事件处理方式有何变化?
2024-11-17 17:10:29 +0800 CST
你可能不知道的 18 个前端技巧
2025-06-12 13:15:26 +0800 CST
小技巧vscode去除空格方法
2024-11-17 05:00:30 +0800 CST
阿里云发送短信php
2025-06-16 20:36:07 +0800 CST
前端如何优化资源加载
2024-11-18 13:35:45 +0800 CST
liunx宝塔php7.3安装mongodb扩展
2024-11-17 11:56:14 +0800 CST
curl错误代码表
2024-11-17 09:34:46 +0800 CST
程序员茄子在线接单