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] 构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(4)- 变更服务,以及第二次重构

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

Summary: 基于 tide + async-graphql + mongodb 构建 Rust 异步 GraphQL 服务的变更服务教程,包括依赖项的更新和配置。以及 async-graphql 简单对象类型、复杂对象类型、输入对象类型的迭代和开发。新用户的插入,包括根据用户唯一性标志属性验证。

Topics: rust graphql async-graphql 变更服务 mutation 查询服务 query

构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(3)- 第一次重构之后,因这段时间事情较多,所以一直未着手变更服务的开发示例。现在私事稍稍告一阶段,让我们一起进行变更服务的开发,以及第二次重构。

一点意外

首先要说,和笔者沟通使用 Tide 框架做 Rust Web 开发的朋友之多,让笔者感到意外。因为 Tide 框架的社区,目前并不活跃,很多 bug 已经拖很久了。大部分实践,笔者接触到的 Rust Web 开发人员,都未选择 Tide 框架

对于使用 Tide 框架做 GraphQL 开发的朋友,笔者有一个基于 tide、async-graphql,以及 mongodb 实现 GraphQL 服务的较完整项目模板,实现了如下功能:

  • 用户注册
  • 使用 PBKDF2 对密码进行加密(salt)和散列(hash)运算
  • 整合 JWT 鉴权的用户登录
  • 密码修改、资料更新
  • 用户查询和变更、项目查询和变更
  • 使用基于 Rust 实现 graphql-client 获取 GraphQL 服务端数据
  • 渲染 GraphQL 数据到 handlebars-rust 模板引擎

更多详细功能请参阅 github 仓库 tide-async-graphql-mongodb,欢迎朋友们参与,共同完善。

另外,基于此模板项目,笔者正在以“三天打鱼,两天晒网”的方式开发一个博客,即本博文发布的站点,也开源在 github 仓库 surfer。同样,欢迎朋友们参与,共同完善。

接下来,让我进行基于 tide + async-graphql + mongodb 开发 GraphQL 服务的第二次重构。

依赖项更新

构建 Rust 异步 GraphQL 服务:基于 tide + async-graphql + mongodb(3)- 第一次重构之后,已经大抵过去一个月时间了。这一个月以来,活跃的 Rust 社区生态,进行了诸多更新:Rust 版本已经为 1.51.0Rust 2021 版即将发布……本示例项目中,使用的依赖项 futuresmongodbbsonserde 等 crate 都有了 1-2 个版本的升级。特别是 async-graphql,在孙老师的辛苦奉献下,版本升级数量达到两位数,依赖项引入方式已经发生了变化。

你可以使用 cargo upgrade 升级,或者直接修改 Cargo.toml 文件,全部使用最新版本的依赖 crate:

[package]
name = "backend"
version = "0.1.0"
authors = ["我是谁?"]
edition = "2018"

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

[dependencies]
futures = "0.3.14"
tide = "0.16.0"
async-std = { version = "1.9.0", features = ["attributes"] }

dotenv = "0.15.0"
lazy_static = "1.4.0"

async-graphql = { version = "2.8.4", features = ["bson", "chrono"] }
mongodb = { version = "1.2.1", default-features = false, features = ["async-std-runtime"] }
bson = "1.2.2"
serde = { version = "1.0.125", features = ["derive"] }

第二次重构:async-graphql 对象类型的使用

在另一个 Rust Web 技术栈示例项目基于 actix-web + async-graphql + rbatis + postgresql / mysql 构建异步 Rust GraphQL 服务(3) - 重构中,代码更为精简一些。因为我们使用了 async-graphql 的简单对象类型、复杂对象类型。

使用简单对象类型

上一篇文章中,我们使用的是 async-graphql普通对象类型,即 ./src/users/models.rs 文件如下所示:

...

pub struct User {
    pub _id: ObjectId,
    pub email: String,
    pub username: String,
    pub cred: String,
}

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

如果在实现 User 类型时,并未有对字段的计算处理,那么这些 gettersetter 方法是否显得很多余?如果我们使用简单对象类型,则可以对代码进行精简,省略这些枯燥的 gettersetter 方法。

use serde::{Serialize, Deserialize};

#[derive(async_graphql::SimpleObject, Serialize, Deserialize, Clone, Debug)]
pub struct User {
    pub _id: ObjectId,
    pub email: String,
    pub username: String,
    pub cred: String,
}

注意,上部分代码块,使用普通对象类型,为了节省篇幅,我们使用 ... 表示省略粘贴部分代码;而使用简单对象类型的下部分代码块,是完整的。需要强调的是:如果对类型字段未有计算处理,使用简单对象类型可以对代码进行精简。

使用复杂对象类型

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

使用普通对象类型

我们需要编写 gettersetter 方法,补充代码如下:

#[async_graphql::Object]
impl User {
    …… 原有字段 `getter`、`setter` 方法

    // 补充如下方法
    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
    }
}

使用复杂对象类型

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

use bson::oid::ObjectId;
use serde::{Deserialize, Serialize};

#[derive(async_graphql::SimpleObject, Serialize, Deserialize, Clone, Debug)]
#[graphql(complex)]
pub struct User {
    pub _id: ObjectId,
    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 的文档中,已经多了一个类型定义:

复杂对象类型 from 定义

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

复杂对象类型 from 查询

变更服务

接下来,我们开发 GraphQL 的变更服务。示例中,我们以模型 -> 服务 -> 总线的顺序来开发。这个顺序并非固定,在实际开发中,可以根据自己习惯进行调整。

定义 NewUser 输入对象类型

在此,我们定义一个欲插入 users 集合中的结构体,包含对应字段即可,其为 async-graphql 中的 输入对象类型。需要注意的是,mongodb 中,_id 是根据时间戳自动生成,因此不需要定义此字段。cred 是计划使用 PBKDF2 对密码进行加密(salt)和散列(hash)运算后的鉴权码,需要定义,但无需在新增是填写。因此,在此我们需要介绍一个 async-graphql 中的标记 #[graphql(skip)],其表示此字段不会映射到 GraphQL。

代码较简单,所以我们直接贴 users/models.rs 文件完整代码:

use bson::oid::ObjectId;
use serde::{Deserialize, Serialize};

#[derive(async_graphql::SimpleObject, Serialize, Deserialize, Clone, Debug)]
#[graphql(complex)]
pub struct User {
    pub _id: ObjectId,
    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
    }
}

#[derive(Serialize, Deserialize, async_graphql::InputObject)]
pub struct NewUser {
    pub email: String,
    pub username: String,
    #[graphql(skip)]
    pub cred: String,
}

编写服务层代码,将 NewUser 结构体插入 MongoDB

服务层 users/services.rs 中,我们仅需定义一个函数,用于将 NewUser 结构体插入 MongoDB 数据库。我们从 GraphiQL/playground 中获取 NewUser 结构体时,因为我们使用了标记 #[graphql(skip)],所以 cred 字段不会映射到 GraphQL。对于 MongoDB 的文档数据库特性,插入是没有问题的。但查询时如果包括 cred 字段,对于不包含此字段的 MongoDB 文档,则需要特殊处理。我们目前仅是为了展示变更服务的实例,所以对于 cred 字段写入一个固定值。随着本教程的逐渐深入,我们会迭代为关联用户特定值,使用 PBKDF2 对密码进行加密(salt)和散列(hash)运算后的鉴权码。

同时,实际应用中,插入用户时,我们应当设定一个用户唯一性的标志属性,以用来判断数据库是否已经存在此用户。本实例中,我们使用 email 作为用户的唯一性标志属性。因此,我们需要开发 get_user_by_email 服务。

再者,我们将 NewUser 结构体插入 MongoDB 数据库后,应当返回插入结果。因此,我们还需要开发一个根据 username 或者 email 查询用户的 GraphQL 服务。因为我们已经设定 email 为用户的唯一性标志属性,因此直接使用 get_user_by_email 查询已经插入用户即可。

MongoDB 数据库的 Rust 驱动使用,本文简要提及,不作详细介绍。

服务层 users/services.rs 文件完整代码如下:

use async_graphql::{Error, ErrorExtensions};
use futures::stream::StreamExt;
use mongodb::Database;

use crate::users::models::{NewUser, User};
use crate::util::constant::GqlResult;

pub async fn all_users(db: Database) -> GqlResult<Vec<User>> {
    let coll = db.collection("users");

    let mut users: Vec<User> = vec![];

    // 查询集合中的所有文档
    let mut cursor = coll.find(None, None).await.unwrap();

    // 数据游标结果迭代
    while let Some(result) = cursor.next().await {
        match result {
            Ok(document) => {
                let user =
                    bson::from_bson(bson::Bson::Document(document)).unwrap();
                users.push(user);
            }
            Err(error) => Err(Error::new("1-all-users").extend_with(|_, e| {
                e.set("details", format!("文档有错:{}", error))
            }))
            .unwrap(),
        }
    }

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

// get user info by email
pub async fn get_user_by_email(db: Database, email: &str) -> GqlResult<User> {
    let coll = db.collection("users");

    let exist_document = coll.find_one(bson::doc! {"email": email}, None).await;

    if let Ok(user_document_exist) = exist_document {
        if let Some(user_document) = user_document_exist {
            let user: User =
                bson::from_bson(bson::Bson::Document(user_document)).unwrap();
            Ok(user)
        } else {
            Err(Error::new("2-email")
                .extend_with(|_, e| e.set("details", "email 不存在")))
        }
    } else {
        Err(Error::new("2-email")
            .extend_with(|_, e| e.set("details", "查询 mongodb 出错")))
    }
}

pub async fn new_user(db: Database, mut new_user: NewUser) -> GqlResult<User> {
    let coll = db.collection("users");

    new_user.email = new_user.email.to_lowercase();

    if self::get_user_by_email(db.clone(), &new_user.email).await.is_ok() {
        Err(Error::new("email 已存在")
            .extend_with(|_, e| e.set("details", "1_EMAIL_EXIStS")))
    } else {
        new_user.cred =
            "P38V7+1Q5sjuKvaZEXnXQqI9SiY6ZMisB8QfUOP91Ao=".to_string();
        let new_user_bson = bson::to_bson(&new_user).unwrap();

        if let bson::Bson::Document(document) = new_user_bson {
            // Insert into a MongoDB collection
            coll.insert_one(document, None)
                .await
                .expect("文档插入 MongoDB 集合时出错");

            self::get_user_by_email(db.clone(), &new_user.email).await
        } else {
            Err(Error::new("3-new_user").extend_with(|_, e| {
                e.set("details", "转换 BSON 对象为 MongoDB 文档时出错")
            }))
        }
    }
}

将服务添加到服务总线

查询服务对应的服务总线为 gql/queries.rs,变更服务对应的服务总线为 gql/mutations.rs。到目前为止,我们一直未有编写变更服务总线文件 gql/mutations.rs。现在,我们将 new_user 变更服务和 get_user_by_email 查询服务分别添加到变更和查询服务总线。

加上我们查询服务的 all_users 服务,服务总线共计 2 个文件,3 个服务。

查询服务总线 gql/queries.rs

use async_graphql::Context;

use crate::dbs::mongo::DataSource;
use crate::users::{self, models::User};
use crate::util::constant::GqlResult;

pub struct QueryRoot;

#[async_graphql::Object]
impl QueryRoot {
    // 获取所有用户
    async fn all_users(&self, ctx: &Context<'_>) -> GqlResult<Vec<User>> {
        let db = ctx.data_unchecked::<DataSource>().db_budshome.clone();
        users::services::all_users(db).await
    }

    //根据 email 获取用户
    async fn get_user_by_email(
        &self,
        ctx: &Context<'_>,
        email: String,
    ) -> GqlResult<User> {
        let db = ctx.data_unchecked::<DataSource>().db_budshome.clone();
        users::services::get_user_by_email(db, &email).await
    }
}

变更服务总线 gql/mutations.rs

use async_graphql::Context;

use crate::dbs::mongo::DataSource;
use crate::users::{
    self,
    models::{NewUser, User},
};
use crate::util::constant::GqlResult;

pub struct MutationRoot;

#[async_graphql::Object]
impl MutationRoot {
    // 插入新用户
    async fn new_user(
        &self,
        ctx: &Context<'_>,
        new_user: NewUser,
    ) -> GqlResult<User> {
        let db = ctx.data_unchecked::<DataSource>().db_budshome.clone();
        users::services::new_user(db, new_user).await
    }
}

第一次验证

查询服务、变更服务均编码完成,我们验证下开发成果。通过 cargo run 或者 cargo watch 启动应用程序,浏览器输入 http://127.0.0.1:8080/v1i,打开 graphiql/playgound 界面。

如果你的配置未跟随教程,请根据你的配置输入正确链接,详见你的 .env 文件配置项。

但是,如果你此时通过 graphiql/playgound 界面的 docs 选项卡查看,仍然仅能看到查询服务下有一个孤零零的 allUsers: [User!]!。这是因为,我们前几篇教程中,仅编写查询服务代码,所以服务器 Schema 构建时使用的是 EmptyMutation。我们需要将我们自己的变更服务总线 gql/mutations.rs,添加到 SchemaBuilder 中。

涉及 gql/mod.rsmain.rs 2 个文件。

将变更服务总线添加到 SchemaBuilder

gql/mod.rs 文件完整代码如下:

pub mod mutations;
pub mod queries;

use crate::util::constant::CFG;
use tide::{http::mime, Body, Request, Response, StatusCode};

use async_graphql::{
    http::{playground_source, receive_json, GraphQLPlaygroundConfig},
    EmptySubscription, Schema,
};

use crate::State;

use crate::dbs::mongo;

use crate::gql::{queries::QueryRoot, mutations::MutationRoot};

pub async fn build_schema() -> Schema<QueryRoot, MutationRoot, EmptySubscription>
{
    // 获取 mongodb datasource 后,可以将其增加到:
    // 1. 作为 async-graphql 的全局数据;
    // 2. 作为 Tide 的应用状态 State;
    // 3. 使用 lazy-static.rs
    let mongo_ds = mongo::DataSource::init().await;

    // The root object for the query and Mutatio, and use EmptySubscription.
    // Add global mongodb datasource  in the schema object.
    // let mut schema = Schema::new(QueryRoot, MutationRoot, EmptySubscription)
    Schema::build(QueryRoot, MutationRoot, EmptySubscription)
        .data(mongo_ds)
        .finish()
}

pub async fn graphql(req: Request<State>) -> tide::Result {
    let schema = req.state().schema.clone();
    let gql_resp = schema.execute(receive_json(req).await?).await;

    let mut resp = Response::new(StatusCode::Ok);
    resp.set_body(Body::from_json(&gql_resp)?);

    Ok(resp.into())
}

pub async fn graphiql(_: Request<State>) -> tide::Result {
    let mut resp = Response::new(StatusCode::Ok);
    resp.set_body(playground_source(GraphQLPlaygroundConfig::new(
        CFG.get("GRAPHQL_PATH").unwrap(),
    )));
    resp.set_content_type(mime::HTML);

    Ok(resp.into())
}

将变更服务总线添加到应用程序作用域状态

main.rs 文件完整代码如下:

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

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

#[async_std::main]
async fn main() -> Result<(), std::io::Error> {
    // tide logger
    tide::log::start();

    // 初始 Tide 应用程序状态
    let schema = build_schema().await;
    let app_state = State { schema: schema };
    let mut app = tide::with_state(app_state);

    // 路由配置
    app.at(CFG.get("GRAPHQL_PATH").unwrap()).post(graphql);
    app.at(CFG.get("GRAPHIQL_PATH").unwrap()).get(graphiql);

    app.listen(format!(
        "{}:{}",
        CFG.get("ADDRESS").unwrap(),
        CFG.get("PORT").unwrap()
    ))
    .await?;

    Ok(())
}

//  Tide 应用程序作用域状态 state.
#[derive(Clone)]
pub struct State {
    pub schema: async_graphql::Schema<
        gql::queries::QueryRoot,
        gql::mutations::MutationRoot,
        async_graphql::EmptySubscription,
    >,
}

Okay,大功告成,我们进行第二验证。

第二次验证

打开方式和注意事项和第一次验证相同。

正常启动后,如果你此时通过 graphiql/playgound 界面的 docs 选项卡查看,将看到查询和变更服务的列表都有了变化。如下图所示:

变更服务 new_user

插入一个新用户(重复插入)

插入的 newUser 数据为(注意,GraphQL 中自动转换为驼峰命名):

注意:示例仅为插入对象部分,你需要补充 mutation 声明和 API 方法。

    newUser: { 
      email: "budshome@budshome.com", 
      username: "我是谁" 
    }

第一次插入,然会正确的插入结果:

{
  "data": {
    "newUser": {
      "cred": "P38V7+1Q5sjuKvaZEXnXQqI9SiY6ZMisB8QfUOP91Ao=",
      "email": "budshome@budshome.com",
      "from": "我是谁<budshome@budshome.com>",
      "id": "608954d900136b6c0041ae09",
      "username": "我是谁"
    }
  }
}

第二次重复插入,因为 email 已存在,则返回我们开发中定义的错误信息:

{
  "data": null,
  "errors": [
    {
      "message": "email 已存在",
      "locations": [
        {
          "line": 2,
          "column": 3
        }
      ],
      "path": [
        "newUser"
      ],
      "extensions": {
        "details": "1_EMAIL_EXIStS"
      }
    }
  ]
}

请自己查看你的数据库,已经正常插入了目标数据。

至此,变更服务开发完成。

因为已经将更为完整的模板项目 tide-async-graphql-mongodb 放在了 github 仓库,所以本教程代码未有放在云上。如果你在实践中遇到问题,需要完成代码包,请联系我(微信号 yupen-com)。

下篇计划

变更服务开发完成后,后端我们告一阶段。下篇开始,我们进行前端的开发,仍然使用 Rust 技术栈:tide、rhai、handlebars-rust、surf,以及 graphql_client。

本次实践,我们称之为 Rust 全栈开发 ;-)

谢谢您的阅读!


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 学习资料 - 泥芹