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

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

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 异步 数据库

推荐文章

Rust 与 sqlx:数据库迁移实战指南
2024-11-19 02:38:49 +0800 CST
如何将TypeScript与Vue3结合使用
2024-11-19 01:47:20 +0800 CST
JS中 `sleep` 方法的实现
2024-11-19 08:10:32 +0800 CST
mysql int bigint 自增索引范围
2024-11-18 07:29:12 +0800 CST
解决 PHP 中的 HTTP 请求超时问题
2024-11-19 09:10:35 +0800 CST
html文本加载动画
2024-11-19 06:24:21 +0800 CST
Nginx 负载均衡
2024-11-19 10:03:14 +0800 CST
LangChain快速上手
2025-03-09 22:30:10 +0800 CST
API 管理系统售卖系统
2024-11-19 08:54:18 +0800 CST
Vue中如何处理异步更新DOM?
2024-11-18 22:38:53 +0800 CST
Gin 与 Layui 分页 HTML 生成工具
2024-11-19 09:20:21 +0800 CST
浅谈CSRF攻击
2024-11-18 09:45:14 +0800 CST
Vue3中的v-for指令有什么新特性?
2024-11-18 12:34:09 +0800 CST
php指定版本安装php扩展
2024-11-19 04:10:55 +0800 CST
php获取当前域名
2024-11-18 00:12:48 +0800 CST
程序员茄子在线接单