NiQin (blog: 泥芹) shared the aphorism --
I returned and saw under the sun, that the race is not to the swift, nor the battle to the strong, neither yet bread to the wise, nor yet riches to men of understanding, nor yet favour to men of skill; but time and chance happeneth to them all. -- 《圣经》

[GraphQL] 基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务(3) - 重构

💥 内容涉及著作权,均归属作者本人。若非作者注明,默认欢迎转载:请注明出处,及相关链接。

Summary: 前 2 篇文章中,我们初始化搭建了工程结构,选择了必须的 crate,并成功构建了 GraphQL 查询服务:从 MySql 中获取了数据,并通过 GraphQL 查询,输出 json 数据。本篇文章,本应当进行 GraphQL 变更(mutation)服务的开发。但是,虽然代码成功运行,却存在一些问题,如:对于 MySql 数据库的连接信息,应当采取配置文件存储;通用公用的代码,应当组织和抽象;诸如此类以便于后续扩展,生产部署等问题。所以,本篇文章中我们暂不进行变更的开发,而是进行第一次简单的重构。以免后续代码扩大,重构工作量繁重。

Topics: rust graphql async-graphql mysql actix-web rbatis

本文 GraphQL 开发部分,受到了 async-graphql 作者孙老师的指导;actix-web 部分,受到了庞老师的指导,非常感谢!

首先,我们通过 shell 命令 cd ./actix-web-async-graphql-rbatis/backend 进入后端工程目录(下文中,将默认在此目录执行操作)。

有朋友提议示例项目的名字中,用的库名也多列一些,方便 github 搜索。虽然关系不大,但还是更名为 actix-web-async-graphql-rbatis。如果您是从 github 检出,或者和我一样命名,注意修改哈。

重构1:配置信息的存储和获取

让我们设想正式生产环境的应用场景:

  • 服务器地址和端口的变更可能;
  • 服务功能升级,对用户暴露 API 地址的变更可能。如 rest api,graphql api,以及版本升级;
  • 服务站点密钥定时调整的可能;
  • 服务站点安全调整,jwt、session/cookie 过期时间的变更可能。

显然易见,我们应当避免每次变更调整时,都去重新编译一次源码——并且,大工程中,Rust 的编译速度让开发者注目。更优的方法是,将这些写入到配置文件中。或许上述第 4 点无需写入,但是文件存储到加密保护的物理地址,安全方面也有提升。

当然,实际的应用场景或许有更合适有优的解决方法,但我们先基于此思路来设计。Rust 中,dotenv crate 用来读取环境变量。取得环境变量后,我们将其作为静态或者惰性值来使用,静态或者惰性值相关的 crate 有 lazy_staticonce_cell 等,都很简单易用。此示例中,我们使用 lazy_static

创建 .env,添加读取相关 crate

增加这 2 个 crate,并且在 backend 目录创建 .env 文件。

cargo add dotenv lazy_static
touch .env

.env 文件中,写入如下内容:

# 服务器信息
ADDRESS=127.0.0.1
PORT=8080

# API 服务信息,“gql” 也可以单独提出来定义
GQL_VER=v1
GIQL_VER=v1i

# 数据库配置
MYSQL_URI=mysql://root:mysql@localhost:3306/budshome

Cargo.toml 文件:

[package]
name = "backend"
version = "0.1.0"
authors = ["zzy <linshi@budshome.com>"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
actix-web = "3.3.2"
actix-rt = "1.1.1"

dotenv = "0.15.0"
lazy_static = "1.4.0"


async-graphql = { version = "2.8.2", features = ["chrono"] }
async-graphql-actix-web = "2.8.2"
rbatis = { version = "1.8.83", default-features = false, features = ["mysql", "postgres"] }

serde = { version = "1.0", features = ["derive"] }

读取配置文件并使用配置信息

对于配置信息的读取和使用,显然属于公用功能,我们将其归到单独的模块中。所以,需要创建 2 个文件:一个是模块标识文件,一个是将抽象出来共用的常量子模块。

cd ./src
mkdir util
touch ./util/mod.rs ./util/constant.rs

至此,本篇文章的所有文件都已经创建,我们确认一下工程结构。

backend 工程结构

  • util/mod.rs,编写如下代码:
pub mod constant;
  • 读取配置信息

util/constant.rs 中,编写如下代码:

use dotenv::dotenv;
use lazy_static::lazy_static;
use std::collections::HashMap;

lazy_static! {
    // CFG variables defined in .env file
    pub static ref CFG: HashMap<&'static str, String> = {
        dotenv().ok();

        let mut map = HashMap::new();

        map.insert(
            "ADDRESS",
            dotenv::var("ADDRESS").expect("Expected ADDRESS to be set in env!"),
        );
        map.insert(
            "PORT",
            dotenv::var("PORT").expect("Expected PORT to be set in env!"),
        );

        map.insert(
            "GQL_PATH",
            dotenv::var("GQL_PATH").expect("Expected GQL_PATH to be set in env!"),
        );
        map.insert(
            "GQL_VER",
            dotenv::var("GQL_VER").expect("Expected GQL_VER to be set in env!"),
        );
        map.insert(
            "GIQL_VER",
            dotenv::var("GIQL_VER").expect("Expected GIQL_VER to be set in env!"),
        );

        map.insert(
            "MYSQL_URI",
            dotenv::var("MYSQL_URI").expect("Expected MYSQL_URI to be set in env!"),
        );

        map
    };
}
  • 重构代码,使用配置信息,正确提供 GraphQL 服务

首先,src/main.rs 文件中引入 util 模块。并用 use 引入 constant 子模块。并重构 HttpServer 的绑定 IP 地址和端口信息,读取其惰性配置值。

mod util;
mod gql;
mod dbs;
mod users;

use actix_web::{guard, web, App, HttpServer};

use crate::util::constant::CFG;
use crate::gql::{build_schema, graphql, graphiql};

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    let schema = build_schema().await;

    println!(
        "GraphQL UI: http://{}:{}",
        CFG.get("ADDRESS").unwrap(),
        CFG.get("PORT").unwrap()
    );

    HttpServer::new(move || {
        App::new()
            .data(schema.clone())
            .service(
                web::resource(CFG.get("GQL_VER").unwrap())
                    .guard(guard::Post())
                    .to(graphql),
            )
            .service(
                web::resource(CFG.get("GIQL_VER").unwrap())
                    .guard(guard::Get())
                    .to(graphiql),
            )
    })
    .bind(format!(
        "{}:{}",
        CFG.get("ADDRESS").unwrap(),
        CFG.get("PORT").unwrap()
    ))?
    .run()
    .await
}

其次,src/gql/mod.rs 文件中,用 use 引入 constant 子模块,读取其惰性配置值。

pub mod mutations;
pub mod queries;

use actix_web::{web, HttpResponse, Result};
use async_graphql::http::{playground_source, GraphQLPlaygroundConfig};
use async_graphql::{EmptyMutation, EmptySubscription, Schema};
use async_graphql_actix_web::{Request, Response};

use crate::util::constant::CFG;
use crate::dbs::mysql::my_pool;
use crate::gql::queries::QueryRoot;

type ActixSchema = Schema<
    queries::QueryRoot,
    async_graphql::EmptyMutation,
    async_graphql::EmptySubscription,
>;

pub async fn build_schema() -> ActixSchema {
    // 获取 mysql 数据池后,可以将其增加到:
    // 1. 作为 async-graphql 的全局数据;
    // 2. 作为 actix-web 的应用程序数据,优势是可以进行原子操作;
    // 3. 使用 lazy-static.rs
    let my_pool = my_pool().await;

    // The root object for the query and Mutatio, and use EmptySubscription.
    // Add global mysql pool in the schema object.
    Schema::build(QueryRoot, EmptyMutation, EmptySubscription)
        .data(my_pool)
        .finish()
}

pub async fn graphql(schema: web::Data<ActixSchema>, req: Request) -> Response {
    schema.execute(req.into_inner()).await.into()
}

pub async fn graphiql() -> Result<HttpResponse> {
    Ok(HttpResponse::Ok().content_type("text/html; charset=utf-8").body(
        playground_source(
            GraphQLPlaygroundConfig::new(CFG.get("GQL_VER").unwrap())
                .subscription_endpoint(CFG.get("GQL_VER").unwrap()),
        ),
    ))
}

最后,不要忘了 src/dbs/mysql.rs 文件中,用 use 引入 constant 子模块,读取其惰性配置值。

use rbatis::core::db::DBPoolOptions;
use rbatis::rbatis::Rbatis;

use crate::util::constant::CFG;

pub async fn my_pool() -> Rbatis {
    let rb = Rbatis::new();

    let mut opts = DBPoolOptions::new();
    opts.max_connections = 100;

    rb.link_opt(CFG.get("MYSQL_URI").unwrap(), &opts).await.unwrap();

    rb
}

配置文件读取已经完成,我们测试看看。这次,我们浏览器中要打开的链接为 http://127.0.0.1:8080/v1i

graphiql ui

执行查询,一切正常。

graphql query

重构2:async-graphql 代码简洁性重构

定义公用类型

在上一篇基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务(2) - 查询服务文章中,gql/queries.rsusers/services.rs 代码中,all_users 函数/方法的返回值为冗长的 std::result::Result<Vec<User>, async_graphql::Error>。显然,这样代码不够易读和简洁。我们简单重构下:定义一个公用的 GqlResult 类型即可。

  • 首先,迭代 util/constant.rs 文件,增加一行:定义 GqlResult 类型别名:
use dotenv::dotenv;
use lazy_static::lazy_static;
use std::collections::HashMap;

pub type GqlResult<T> = std::result::Result<T, async_graphql::Error>;

lazy_static! {
    // CFG variables defined in .env file
    pub static ref CFG: HashMap<&'static str, String> = {
        dotenv().ok();

        let mut map = HashMap::new();

        map.insert(
            \"ADDRESS\",
            dotenv::var(\"ADDRESS\").expect(\"Expected ADDRESS to be set in env!\"),
        );
 ……
 ……
 ……
  • 其次,迭代 gql/queries.rsusers/services.rs 文件,引入并让函数/方法返回 GqlResult 类型。

gql/queries.rs

use async_graphql::Context;
use rbatis::rbatis::Rbatis;

use crate::util::constant::GqlResult;
use crate::users::{self, models::User};
pub struct QueryRoot;

#[async_graphql::Object]
impl QueryRoot {
    // Get all Users
    async fn all_users(&self, ctx: &Context<'_>) -> GqlResult<Vec<User>> {
        let my_pool = ctx.data_unchecked::<Rbatis>();
        users::services::all_users(my_pool).await
    }
}

users/services.rs

use async_graphql::{Error, ErrorExtensions};
use rbatis::rbatis::Rbatis;
use rbatis::crud::CRUD;

use crate::util::constant::GqlResult;
use crate::users::models::User;

pub async fn all_users(my_pool: &Rbatis) -> GqlResult<Vec<User>> {
    let users = my_pool.fetch_list::<User>("").await.unwrap();

    if users.len() > 0 {
        Ok(users)
    } else {
        Err(Error::new("1-all-users")
            .extend_with(|_, e| e.set("details", "No records")))
    }
}

执行查询,一切正常。

async-graphql 对象类型重构

此重构受到了 async-graphql 作者孙老师的指导,非常感谢!孙老师的 async-graphql 项目仓库在 github,是 Rust 生态中最优秀的 GraphQL 服务端库,希望朋友们去 starfork

目前代码是可以运行的,但是总是感觉太冗余。比如 impl User 中大量的 getter 方法,这是老派的 Java 风格了。在 async-graphql 中,已经对此有了解决方案 SimpleObject,大家直接删去 impl User 即可。如下 user/models.rs 代码,是可以正常运行的:

async-graphql 简单对象类型

use serde::{Serialize, Deserialize};


#[rbatis::crud_enable]
#[derive(async_graphql::SimpleObject, Serialize, Deserialize, Clone, Debug)]
pub struct User {
    pub id: i32,
    pub email: String,
    pub username: String,
    pub cred: String,
}

// 下段代码直接不要,删除或注释
// #[async_graphql::Object]
// impl User {
//     pub async fn id(&self) -> i32 {
//         self.id
//     }
// 
//     pub async fn email(&self) -> &str {
//         self.email.as_str()
//     }
// 
//     pub async fn username(&self) -> &str {
//         self.username.as_str()
//     }
// }

这个 user/models.rs文件时完整的,请您删掉或者注释后,运行测试一次是否正常。

这个派生属性,在 async-graphql 中称之为简单对象,主要是省去满篇的 gettersetter

async-graphql 复杂对象类型

但有时,除了自定义结构体中的字段外,我们还需要返回一些计算后的数据。比如,我们要在邮箱应用中,显示发件人信息,一般是 username<email> 这样的格式。对此实现有两种方式:

第 1 种方式:async-graphql::Object 类型

使用 async-graphql::Object 类型。完善昨天的代码为(注意省略的部分不变):

use serde::{Serialize, Deserialize};

#[rbatis::crud_enable]
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct User {
    pub id: i32,
    pub email: String,
    pub username: String,
    pub cred: String,
}

#[async_graphql::Object]
impl User {
    pub async fn id(&self) -> i32 {
        self.id
    }

    pub async fn email(&self) -> &str {
        ……
        ……
        ……

    // 补充如下方法
    pub async fn from(&self) -> String {
        let mut from =  String::new();
        from.push_str(&self.username);
        from.push_str("<");
        from.push_str(&self.email);
        from.push_str(">");

        from
    }
}

第 2 种方式,async_graphql::ComplexObject 类型

async-graphql 的新版本中,可以将复杂对象类型和简单对象类型整合使用。这样,既可以省去省去满篇的 gettersetter,还可以自定义对结构体字段计算后的返回数据。如下 users/models.rs 文件,是完整的代码:

use serde::{Serialize, Deserialize};

#[rbatis::crud_enable]
#[derive(async_graphql::SimpleObject, Serialize, Deserialize, Clone, Debug)]
#[graphql(complex)]
pub struct User {
    pub id: i32,
    pub email: String,
    pub username: String,
    pub cred: String,
}

#[async_graphql::ComplexObject]
impl User {
    pub async fn from(&self) -> String {
        let mut from =  String::new();
        from.push_str(&self.username);
        from.push_str("<");
        from.push_str(&self.email);
        from.push_str(">");

        from
    }
}

我们可以看到,GraphQL 的文档中,已经多了一个类型定义:

复杂对象类型

执行查询,我们看看返回结果:

{
  "data": {
    "allUsers": [
      {
        "cred": "5ff82b2c0076cc8b00e5cddb",
        "email": "ok@budshome.com",
        "from": "我谁24ok32<ok@budshome.com>",
        "id": 1,
        "username": "我谁24ok32"
      },
      {
        "cred": "5ff83f4b00e8fda000e5cddc",
        "email": "oka@budshome.com",
        "from": "我s谁24ok32<oka@budshome.com>",
        "id": 2,
        "username": "我s谁24ok32"
      },
      {
        "cred": "5ffd710400b6b84e000349f8",
        "email": "oka2@budshome.com",
        "from": "我2s谁24ok32<oka2@budshome.com>",
        "id": 3,
        "username": "我2s谁24ok32"
      }
    ]
  }
}

重构3:actix-web 代码整理

依赖项整理

此重构受到了庞老师的指点,非常感谢!

上一篇文章,服务器启动主程序时,我们可以使用 #[actix_web::main] 替代 #[actix_rt::main]。这种情况下,backend/Cargo.toml 文件中的依赖项 actix-rt 也可以直接删除。

路由组织

目前,GraphQL Schema 和路由,我们都直接在 main.rs 文件中注册到了 actix-web HttpServer 对象。笔者个人喜欢 main.rs 代码尽可能简单清晰——不是代码量越少越好,比如,GraphQL Schema 和路由,完全可以放在 gql 模块中,以后多了一个 rest 模块之类,各自模块中定义路由。

对此也应当重构,但实例简单,我们在此后端开发中仅作提及。在未来的前端开发中(使用 actix-web + surf + graphql-client + rhai + handlebars-rust 技术栈),因为需要复杂的路由,我们再做处理。

第一次重构,我们就到这个程度。下一篇,我们将进行 GraphQL 变更(mutation)的开发。

实例源码仓库在 github,欢迎您共同完善。


欢迎交流(指正、错别字等均可)。我的联系方式为页底邮箱,或者微信号 yupen-com。

yupen-com

谢谢您的阅读。


Related Articles

  1. [Rust] RustHub.org:基于 Rust-Web 技术栈,及 image-rs、fluent-rs、rhai-script ……
  2. [WebAssembly] yew SSR 服务器端渲染
  3. [Rust] async-std 创建者对于最近“项目是否已死?”,移除对其支持等的答复
  4. [Rust] Rust 1.56.0 版本和 Rust 2021 版次发布,新特性一览,及项目的迁移、升级
  5. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 WebAssembly 博客应用的体验报告
  6. [Rust] Rust 官方周报 399 期(2021-07-14)
  7. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 web 前端(5)- 构建 HTTP 请求、与外部服务器通信的两种方法
  8. [Rust] Rust 官方周报 398 期(2021-07-07)
  9. [Rust] Rust 官方周报 397 期(2021-06-30)
  10. [Rust] Rust 官方周报 396 期(2021-06-23)
  11. [Rust] Rust 官方周报 395 期(2021-06-16)
  12. [Rust] Rust 1.53.0 明日发布,关键新特性一瞥
  13. [Rust] 使用 tide、handlebars、rhai、graphql 开发 Rust web 前端(3)- rhai 脚本、静态/资源文件、环境变量等
  14. [Rust] 使用 tide、handlebars、rhai、graphql 开发 Rust web 前端(2)- 获取并解析 GraphQL 数据
  15. [Rust] 使用 tide、handlebars、rhai、graphql 开发 Rust web 前端(1)- crate 选择及环境搭建
  16. [Rust] Rust 官方周报 394 期(2021-06-09)
  17. [Rust] Rust web 前端库/框架评测,以及和 js 前端库/框架的比较
  18. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 web 前端(4)- 获取 GraphQL 数据并解析
  19. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 web 前端(3)- 资源文件及小重构
  20. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 WebAssembly 标准的 web 前端(2)- 组件和路由
  21. [WebAssembly] Rust 和 Wasm 的融合,使用 yew 构建 WebAssembly 标准的 web 前端(1)- 起步及 crate 选择
  22. [Rust] Rust 官方周报 393 期(2021-06-02)
  23. [Rust] Rust 官方周报 392 期(2021-05-26)
  24. [Rust] Rust 中,对网址进行异步快照,并添加水印效果的实践
  25. [Rust] Rust 官方周报 391 期(2021-05-19)
  26. [Rust] Rust,风雨六载,砥砺奋进
  27. [Rust] 为什么我们应当将 Rust 用于嵌入式开发?
  28. [Rust] Rust 官方周报 390 期(2021-05-12)
  29. [Rust] Rust + Android 的集成开发设计
  30. [Rust] Rust 1.52.1 已正式发布,及其新特性详述
  31. [Rust] 让我们用 Rust 重写那些伟大的软件吧
  32. [Rust] Rust 1.52.0 已正式发布,及其新特性详述
  33. [Rust] Rust 官方周报 389 期(2021-05-05)
  34. [GraphQL] 基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务(4) - 变更服务,以及小重构
  35. [Rust] Rust 1.52.0 稳定版预发布测试中,关键新特性一瞥
  36. [Rust] Rust 生态中,最不知名的贡献者和轶事
  37. [Rust] Rust 基金会迎来新的白金会员:Facebook
  38. [Rust] Rustup 1.24.1 已官宣发布,及其新特性详述
  39. [Rust] Rust 官方周报 388 期(2021-04-28)
  40. [Rust] Rust 官方周报 387 期(2021-04-21)
  41. [GraphQL] 构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(4)- 变更服务,以及第二次重构
  42. [Rust] Rustup 1.24.0 已官宣发布,及其新特性详述
  43. [Rust] basedrop:Rust 生态中,适用于实时音频的垃圾收集器
  44. [Rust] Rust 编译器团队对成员 Aaron Hill 的祝贺
  45. [Rust] Jacob Hoffman-Andrews 加入 Rustdoc 团队
  46. [机器人] 为什么应将 Rust 引入机器人平台?以及机器人平台的 Rust 资源推荐
  47. [Rust] rust-lang.org、crates.io,以及 docs.rs 的管理,已由 Mozilla 转移到 Rust 基金会
  48. [Rust] Rust 官方周报 386 期(2021-04-14)
  49. [Rust] Rust 编译器(Compiler)团队 4 月份计划 - Rust Compiler April Steering Cycle
  50. [GraphQL] 基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务(3) - 重构
  51. [Rust] 头脑风暴进行中:Async Rust 的未来熠熠生辉
  52. [GraphQL] 基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务(2) - 查询服务
  53. [GraphQL] 基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务 - 起步及 crate 选择
  54. [Rust] Rust 2021 版本特性预览,以及工作计划
  55. [Rust] Rust 用在生产环境的 42 家公司
  56. [Rust] 构建最精简的 Rust Docker 镜像
  57. [Rust] Rust 官方周报 385 期(2021-04-07)
  58. [Rust] 使用 Rust 做异步数据采集的实践
  59. [Rust] Android 支持 Rust 编程语言,以避免内存缺陷
  60. [Rust] Android 平台基础支持转向 Rust
  61. [Rust] Android 团队宣布 Android 开源项目(AOSP),已支持 Rust 语言来开发 Android 系统本身
  62. [Rust] RustyHermit——基于 Rust 实现的下一代容器 Unikernel
  63. [Rust] Rustic:完善的纯粹 Rust 技术栈实现的国际象棋引擎,多平台支持(甚至包括嵌入式设备树莓派 Raspberry Pi、Buster)
  64. [Rust] Rust 迭代器(Iterator trait )的要诀和技巧
  65. [Rust] 使用 Rust 极致提升 Python 性能:图表和绘图提升 24 倍,数据计算提升 10 倍
  66. [Rust] 【2021-04-03】Rust 核心团队人员变动
  67. [Rust] Rust web 框架现状【2021 年 1 季度】
  68. [Rust] Rust 官方周报 384 期(2021-03-31)
  69. [Rust] Rust 中的解析器组合因子(parser combinators)
  70. [生活] 毕马威(KPMG)调查报告:人工智能的实际采用,在新冠疫情(COVID-19)期间大幅提升
  71. [Python] HPy - 为 Python 扩展提供更优秀的 C API
  72. [Rust] 2021 年,学习 Rust 的网络资源推荐(2)
  73. [Rust] 2021 年,学习 Rust 的网络资源推荐
  74. [生活] 况属高风晚,山山黄叶飞——彭州葛仙山露营随笔
  75. [Rust] Rust 1.51.0 已正式发布,及其新特性详述
  76. [Rust] 为 Async Rust 构建共享的愿景文档—— Rust 社区的讲“故事”,可获奖
  77. [Rust] Rust 纪元第 382 周最佳 crate:ibig 的实践,以及和 num crate 的比较
  78. [Rust] Rust 1.51.0 稳定版本改进介绍
  79. [Rust] Rust 中将 markdown 渲染为 html
  80. [生活] 国民应用 App 的用户隐私数据窥探
  81. [GraphQL] 构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(3)- 重构
  82. [GraphQL] 构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(2)- 查询服务
  83. [GraphQL] 构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(1)- 起步及 crate 选择
  84. [Rust] Rust 操控大疆可编程 tello 无人机

Topics

rust(84)

graphql(17)

rust-官方周报(17)

webassembly(16)

wasm(10)

tide(9)

async-graphql(9)

yew(9)

rust-web(8)

rust-官方博客(8)

this-week-in-rust(6)

mysql(5)

actix-web(5)

rbatis(5)

android(4)

mongodb(3)

json-web-token(3)

jwt(3)

cargo(3)

技术延伸(3)

rust-wasm(3)

trunk(3)

handlebars(3)

rhai(3)

async-std(3)

用户隐私(2)

学习资料(2)

python(2)

ai(2)

人工智能(2)

postgresql(2)

rust-compiler(2)

rust-基金会(2)

rust-foundation(2)

rustup(2)

rust-toolchain(2)

rust-工具链(2)

rust-游戏开发(2)

rust-区块链(2)

rust-2021(2)

graphql-client(2)

surf(2)

rust-game(2)

rusthub(2)

tello(1)

drone(1)

无人机(1)

隐私数据(1)

markdown(1)

html(1)

crate(1)

async(1)

异步(1)

旅游(1)

不忘生活(1)

葛仙山(1)

hpy(1)

python-扩展(1)

正则表达式(1)

解析器组合因子(1)

组合器(1)

regular-expression(1)

parser-combinator(1)

regex(1)

官方更新(1)

rust-工作招聘(1)

rust-技术资料(1)

rust-周最佳-crate(1)

rust-web-框架(1)

rust-web-framework(1)

rust-核心团队(1)

rust-core-team(1)

rust-language-team(1)

pyo3(1)

rust-python-集成(1)

python-性能改进(1)

迭代器(1)

iterator-trait(1)

国际象棋(1)

chess(1)

游戏引擎(1)

game-engine(1)

虚拟化(1)

unikernel(1)

rustyhermit(1)

linux(1)

virtualization(1)

sandboxing(1)

沙箱技术(1)

数据采集(1)

异步数据采集(1)

docker(1)

镜像(1)

生产环境(1)

rust-评价(1)

rust-2021-edition(1)

rust-2021-版本(1)

graphql-查询(1)

vision-doc(1)

愿景文档(1)

代码重构(1)

steering-cycle(1)

方向周期(1)

隐私声明(1)

机器人(1)

robotics(1)

rustdoc(1)

rust-编译器(1)

实时音频(1)

real-time-audio(1)

变更服务(1)

mutation(1)

查询服务(1)

query(1)

rust-贡献者(1)

rust-轶事(1)

rust-稳定版(1)

rust-预发布(1)

rust-测试(1)

安全编程(1)

可信计算(1)

安全代码(1)

secure-code(1)

rust-android-integrate(1)

rust-embedded(1)

rust-嵌入式(1)

rust-生产环境(1)

rust-production(1)

网页快照(1)

网页截图(1)

水印效果(1)

图片水印(1)

yew-router(1)

css(1)

web-前端(1)

wasm-bindgen(1)

区块链(1)

blockchain(1)

dotenv(1)

标识符(1)

rust-1.53.0(1)

rust-1.56.0(1)

rust-项目升级(1)

异步运行时(1)

ssr(1)

tokio(1)

warp(1)

reqwest(1)

graphql-rust(1)


Elsewhere

- Open Source
  1. github/zzy
  2. github/sansx
- Learning & Studying
  1. Rust 学习资料 - 泥芹