我用 Rust 写了一个 Hello World

入职第一天,老板要试一下我的水平,于是给我布置了一个任务,写一个 Hello World吧,但是公司用的是 Rust。

我心想,这不是分分钟的事情嘛,很快程序就写好了:

fn main() {
     println!("Hello World!")
}

然后老板说,前端怎么对接你这个接口?啊,你不早说,是写一个接口啊。行吧,那就用 Axum 框架写一个吧,也不是啥难事。虽然不是分分钟,但是三五分钟还是可以搞定的:

use axum::Router;
use axum::routing::get;

#[tokio::main]
async fn main() {
     let app = Router::new().route("/", async { "Hello World"});
     let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();     axum::serve(listener, app).await.unwrap();
}

然后和老板去回报进度:老板,接口版本的 Hello World 完成了。只见老板眉头一皱:小苏,做一个事情要考虑周全,你这个版本虽然完成了功能,有没有想过安全性呢?给返回 Hello World 加密!

啥!Hello World 也要加密啊,行吧。那就上 AES256-GCM 吧,军规级的,够安全了吧?

use aes_gcm::{Aes256Gcm, Key, KeyInit, Nonce};
use hex::encode;

#[tokio::main]
async fn main() {
     let app = Router::new().route("/", get(encrypted_hello)); 
     // 省略没有改变的代码
}

async fn encrypted_hello() -> String {
     let message = b"Hello, World!"; 
     // 省略三五行加密的模版代码,只是为了说明这里实现了加密
     let ciphertext = cipher.encrypt(nonce, message.as_ref()).unwrap();
     format!("encrypted: {}", encode(&ciphertext))
}

心里想,这回老板应该满意了吧。满心欢喜地找老板交差,仿佛升职加薪迎娶老板女儿的日子马上就要到来了。

给老板演示之后,老板欣然一笑,转而又语重心长地说道:安全是安全了,只是……我的心提到了嗓子眼,只是啥?!

老板停顿了好一会,继续说道:只是你有没有考虑过,Axum 是一个协程框架,你将 CPU 密集的计算放在协程里,会不会阻塞?

天啊,流氓不可怕,啊不,老板不可怕,只怕老板懂技术,似懂非懂最可怕。我就写一个 Hello World 而已啊,需要那么复杂嘛?!行,我接着改。

从老板办公室到工位这一路有点漫长,我的脑子飞快地转动着,Axum 框架是基于 Tokio 协程运行时,Tokio 好像提供了一个 API,可以让将一些密集型的代码放到线程里执行……啊,想到了 task::spawn_blocking()!

async fn encrypted_hello() -> String {
     task::spawn_blocking(|| { let message = b"Hello, World!"; 
    // 继续省略
     let ciphertext = cipher.encrypt(nonce, message.as_ref()).unwrap();  
    format!("encrypted: {}", encode(&ciphertext))}).await.unwrap()
}

走,去老板办公室,我得意地一笑,你难不倒我,你女儿迟早是我的。“老板,我想到办法了,把加密的重活交给 tokio 的线程去执行,就不会阻塞协程了!” 老板,看了一眼我,说道:

“你没看到我和小米同学在谈公事嘛,你先出去!”

我瞟了一眼,小米同学是挺漂亮……感性的……

过了大半个小时,老板来到我的工位前,对我说:“这个版本兼顾了安全和性能,还是不错的,但是……”。老板怎么总是喜欢停顿加但是呢?

“但是,你有没有想过,你把密集型计算委托给 Tokio,如果大家都这么做的话,会导致公共线程池饱和……”。老板,这些是 AI 和你说的嘛?

没办法,继续改,既然不能用公共线程池来做,就要自己启用一个线程,然后和协程之间通过 Channel 进行通信……好像有一个东西叫做 mpsc……我只是写一个 Hello World 啊!天啊,迎娶老板女儿啊,不是西天取经啊。

use std::thread;
use aes_gcm::{Aes256Gcm, Key, KeyInit, Nonce};
use aes_gcm::aead::Aead;
use axum::Router;
use axum::routing::get;
use hex::encode;
use rand::RngCore;
use rand::rngs::OsRng;
use tokio::sync::{mpsc, oneshot};

struct EncryptTask {
 message: Vec<u8>, responder: oneshot::Sender<String>}

#[tokio::main]
async fn main() {
     let (tx, mut rx) = mpsc::channel::<EncryptTask>(100);
     thread::spawn(move || { 
         while let Some(task) = rx.blocking_recv() { 
            let key = Key::<Aes256Gcm>::from_slice(&[0u8; 32]);
            let cipher = Aes256Gcm::new(key);
             let mut nonce_bytes = [0u8; 12]; 
            OsRng.fill_bytes(&mut nonce_bytes); 
            let nonce = Nonce::from_slice(&nonce_bytes);
             let ciphertext = cipher.encrypt(nonce, task.message.as_ref()).unwrap();
             let result = format!("nonce: {}, chipher_text: {}", encode(&nonce), encode(&ciphertext));
             let _ = task.responder.send(result); 
        } 
    });
    let app = Router::new().route("/", get(move || encrypted_hello(tx.clone())));
     let listener = tokio::net::TcpListener::bind("0.0.0.0:8080").await.unwrap();             axum::serve(listener, app).await.unwrap();
 }

async fn encrypted_hello(tx: mpsc::Sender<EncryptTask>) -> String {
     let (resp_tx, resp_rx) = oneshot::channel(); let task = EncryptTask { message: b"Hello World".to_vec(), responder: resp_tx, }; tx.send(task).await.unwrap();
     resp_rx.await.unwrap()
 }

经过一番折腾,终于完成了。看了一眼窗外,一天过去了,天都黑了。老板和小米同学一前一后走出办公室,正要下班。我抓紧喊了一声,老板我完成了!

老板对我挥了挥手,说道:我想了一下,Hello World 没必要做的那么复杂,你还是改回原来的版本吧…..

本作品采用《CC 协议》,转载必须注明作者和本文链接
讨论数量: 2

被这个小故事给吸引了 :+1: :joy:

3周前 评论
苏近之 (楼主) 3周前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!