有个特别扎心的比喻,我看完直接点头到脖子酸:

以前的 Rust 求职市场像村口赶集。摊位不多,但熟人多,大家看你搬得动货、找得着零钱、秤砣不作假,就愿意让你来试两天。

现在呢?摊位是多了点,但门口多了保安、闸机、IQ 测试、三篇作文、四周流程……你还没见到卖菜的大叔,先把自己写成了“未来独角兽拯救者”。

灵感来自 Ajay Kumar 的 Medium 文章《I Miss The Old Rust Job Market So Bad》(2025-12)。我不复读他的段子,咱把它翻译成一套能用的打法:怎么把“求职”从内耗拉回“工程”。

农贸市场:门槛不高,但看重真本事

发生了啥:Rust 没变,流程变了

先把结论放桌上:Rust 的工作内容其实没怎么变,变的是激励机制,最后变成了流程加戏。

给你 5 个“人话版本”的解释:

  1. 风险从公司转移到候选人:以前是“先来试试,不行再说”,现在是“你先证明自己是全能天才,还最好别花我一分钱”。
  2. 合规表演:创业公司学大厂流程,觉得步骤多等于严谨,结果变成“仪式感工程”。
  3. 代理指标上位:作文、刷题、IQ 测试、各种 percentile……方便打分,但对“能不能把线上事故变成无聊的小事”几乎没帮助。
  4. Rust 变主流以后,竞争密度变了:更多人会 Rust,招聘更怕踩坑,于是“多筛几轮”成了默认动作。
  5. 最讽刺的是:真正的 Rust 能力不在迷宫里:所有权边界、错误设计、可观测性、背压、测试哲学、性能分析……这些都得在真实代码里看。

如果你觉得自己像在“闯关”,不是你太玻璃心,是流程真的在加戏。

一套“反马戏团”的求职打法:把信号做硬,把边界说清

你改变不了行业,但你能改变自己的漏斗:让对方 10 分钟就能看到你的信号;让无意义环节在你这里止损。

第一步:做一页“Rust Proof”,别让人去考古

不要只给简历。给一个“作品入口页”,像你开摊位把菜摆整齐:

  • 1 个小服务:axum / actix-web + sqlx(带迁移),README 讲清 trade-off
  • 1 个 CLI:clap + serde + anyhow + tracing(能安装、能用、能看日志)
  • 1 个“所有权设计”示例:刻意做到“少 clone、边界清楚、生命周期能解释”
  • 1 个“会测、会量”的证据:一个 property-based test 或一个 criterion benchmark

重点不是堆技术栈,重点是把“我怎么做取舍”写出来。Rust 面试真正看的就是这个。

第二步:把 take-home 换成 live pairing(礼貌,但坚定)

你不需要硬刚,你只需要把话术准备好,复制粘贴就能用:

标题:关于 take-home 的替代方案

谢谢你们的题目。我更愿意做一次 60 分钟的 live pairing,基于你们真实代码做一件小事:加一个 endpoint、重构一个并发原语、补一个属性测试、或 profile 一段热点路径。
这样你们拿到的信号更真实,也更省双方时间。
如果 take-home 必须做,能否把范围 timebox 到 60 分钟,我做完再当面讲清取舍?

很多团队会答应。拒绝的也没关系:你等于提前过滤掉“会把你周末当免费劳动力”的岗位。

第三步:准备三套“可教学”的回答,别背八股

你需要的不是背定义,是能把概念讲成工程决策:

  • 所有权与并发:什么时候用 Arc<Mutex<_>>,什么时候用 channel,背压怎么落地
  • 错误处理anyhow::Result 什么时候合适,thiserror 怎么把领域错误说清楚
  • 可观测性与性能:先上 tracing 再优化;用 flamegraph/bench 让“快”有证据

面试官问这些,不是在考你“会不会”,是在看你“能不能带着别人一起会”。

第四步:优化漏斗:先找问题,再找岗位

冷投就像在大卖场里吆喝,嗓子会先哑。更有效的是“围着问题走”:

  • 先看团队在公开仓库里真正头疼什么:慢、抖、内存爆、并发打架、测试不稳
  • 去做一个小 PR 或文档补丁,再带着上下文去沟通
  • 你的开场不是“我想应聘”,而是“你们这个 p99 我看着难受,我做了个小 demo”

标题不如信号,信号不如证据。

第五步:设边界:把“不”说成流程的一部分

我个人会写在备忘录里,面试前先读一遍:

  • 不做超过 60 分钟的无偿作业
  • 不接受“谜题/IQ 测试”当硬门槛
  • 四周以上流程,除非薪酬和使命对得起我的时间

你不是在“求一个恩赐”,你是在做一次合作筛选。

60 分钟小作品:一个带背压的 axum endpoint(面试配对编程同款)

你可以把它当“Rust Proof”里的一个摊位:代码不大,但信号很硬——异步、背压、日志、取舍全在里面。

1) 起个项目

cargo new rust-proof-axum-backpressure
cd rust-proof-axum-backpressure

Cargo.toml 加依赖:

[dependencies]
axum = "0.7"
tokio = { version = "1", features = ["full"] }
tracing = "0.1"
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

2) 写个“最多同时处理 8 个请求”的 /work

src/main.rs

use axum::{routing::get, Router};
use std::{sync::{Arc, atomic::{AtomicUsize, Ordering}}, time::Duration};
use tokio::sync::Semaphore;
use tracing::{info, Level};
use tracing_subscriber::{fmt, EnvFilter};

#[derive(Clone)]
struct AppState {
    permits: Arc<Semaphore>,
    inflight: Arc<AtomicUsize>,
}

#[tokio::main]
async fn main() {
    fmt()
        .with_env_filter(EnvFilter::from_default_env().add_directive(Level::INFO.into()))
        .init();

    let state = AppState {
        permits: Arc::new(Semaphore::new(8)),
        inflight: Arc::new(AtomicUsize::new(0)),
    };

    let app = Router::new()
        .route("/work", get(work))
        .with_state(state);

    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
    info!("listening on http://127.0.0.1:3000/work");
    axum::serve(listener, app).await.unwrap();
}

async fn work(state: axum::extract::State<AppState>) -> String {
    let _permit = state.permits.clone().acquire_owned().await.unwrap();
    let now = state.inflight.fetch_add(1, Ordering::SeqCst) + 1;
    info!(inflight = now, "start work");

    tokio::time::sleep(Duration::from_millis(250)).await;

    let now = state.inflight.fetch_sub(1, Ordering::SeqCst) - 1;
    info!(inflight = now, "finish work");
    "ok\n".to_string()
}

这段代码背后表达的取舍很直白:当压力上来时,我宁愿让请求排队,也不愿意把 CPU 打爆、把延迟打成心电图。

3) 跑起来,看“背压”有没有真的发生

RUST_LOG=info cargo run

另开一个终端压一下:

for i in {1..30}; do curl -s http://127.0.0.1:3000/work >/dev/null & done; wait

你会在日志里看到 inflight 最高不会超过 8。这个就是背压在工作:请求来了,但系统不乱跑。

面试的时候,你可以顺手聊两句扩展:要不要加超时?要不要返回 429?要不要按 tenant 做配额?这都是工程问题,不是谜题。

常见坑:别把自己耗成“面试生成器”

  1. 作文工厂:写三篇“热爱公司文化”的长文,最后连面试都没有。解决:作品页 + 30 秒 pitch,能压就压。
  2. 周末被偷走:take-home 一做就是两天,还要你“顺便写个文档”。解决:timebox 60 分钟,超过就换 live pairing。
  3. 把刷题当主线:刷题可以练手,但 Rust 真正的信号在真实代码里。解决:把时间投到可读、可测、可观测的项目上。
  4. 只讲“我会 Rust”:这句话在 2025 没什么含金量了。解决:讲“我怎么让代码更无聊、更可靠”,并拿证据给对方看。

三行总结 + 30 天行动清单

三行总结:

  • Rust 工作没变,流程变“花”了;别跟花活死磕。
  • 让对方 10 分钟看到你的证据,比你写 10 页自我介绍更有用。
  • 设边界不是耍脾气,是把时间花在值得的合作上。

30 天行动清单(照做就行):

  • 第 1 周:做出一页 “Rust Proof”,补 1 个测试 + 1 个 benchmark,写 600 字设计笔记
  • 第 2 周:10 个温和的 warm intro(同事、维护者、社群熟人),带着“我做了啥”去聊
  • 第 3 周:面试期间优先 live pairing;把回答讲成工程决策,而不是背书
  • 第 4 周:给目标团队一个小 demo/README diff,明确要 timeline,别被无期限拖着

参考阅读

  • Ajay Kumar: I Miss The Old Rust Job Market So Bad(Medium, 2025-12)

觉得这篇文章有用吗?

  1. 点赞:如果觉得有帮助,点个赞让更多人看到
  2. 转发:分享给可能需要的朋友或同事
  3. 关注:关注梦兽编程,不错过更多实用技术文章
  4. 留言:你遇到过哪些离谱面试流程?你是怎么止损的?评论区聊聊

你的支持是我持续创作的最大动力!