资讯动态

Windmill 中的 Rust 脚本开发完全指南:main 函数约定、依赖声明与异步执行

发布时间:2026/9/14 11:23:44 来源:尧图企业网站定制
Windmill 中的 Rust 脚本开发完全指南main 函数约定、依赖声明与异步执行【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmillWindmill 是一个将脚本转化为 Webhook、工作流与 UI 的开发者平台Rust 是它原生支持的高级脚本语言之一worker 标签rust见 迁移脚本。本文以官方语言规范文档 system_prompts/languages/rust.md 为主体结合 worker 执行器与解析器的源码实现系统讲解在 Windmill 中编写、运行与调试 Rust 脚本的完整方法。读完本文你将掌握 Rust 脚本的标准结构、内嵌 Cargo 依赖声明语法、异步任务的正确写法以及脚本从编写、预览到部署的完整命令行工作流。一、Rust 脚本在 Windmill 中的定位在 Windmill 中Rust 与 Python、TypeScript、Go、Bash 等语言并列是一种脚本即代码的执行形态你写的不是完整可执行二进制而是一个必须导出main函数的源码单元。平台侧的 worker 会在每次运行前自动完成依赖解析、编译、缓存与沙箱执行开发者只需要专注业务逻辑本身。该语言能力由以下组件共同支撑rust_executor.rsworker 端的 Rust 执行器负责生成 Cargo 工程、编译、运行并收集结果windmill-parser-rustRust 脚本解析器负责提取main函数签名用于生成参数 UI与内嵌依赖清单用于生成Cargo.tomlCargo.toml.default默认清单模板内置了serde与serde_jsonadd_rust_lang 迁移向SCRIPT_LANG枚举注册rust并把它加入默认 worker 标签。二、脚本结构main 函数是唯一入口Rust 脚本必须包含一个名为main的函数并带有正确的返回类型。官方规范文档给出的标准骨架如下use anyhow::anyhow; use serde::Serialize; #[derive(Serialize, Debug)] struct ReturnType { result: String, count: i32, } fn main(param1: String, param2: i32) - anyhow::ResultReturnType { Ok(ReturnType { result: param1, count: param2, }) }三条硬性约定规范文档明确强调main函数必须遵守参数使用拥有所有权的类型owned types如String、i32而不是str这类借用引用。这是因为 worker 会将 JSON 参数反序列化后按值传入。返回类型必须可序列化即实现#[derive(Serialize)]。脚本返回值最终会被序列化为 JSON供后续流程步骤通过results.step_id引用。返回类型包装在anyhow::ResultT中Err分支用于表达脚本失败错误信息会出现在执行日志里。源码层面的印证解析器 parse_rust_signature 用syn解析源码 AST找到名为main的函数后遍历其参数为每个参数生成Arg { name, otyp, typ, ... }。其中otyp保留原始 Rust 类型文本如Vec u8 而typ则通过 parse_pat_type 归一化为一套 Windmill 内部类型系统Rust 参数类型Windmill 类型Typ说明i8~i128、u8~u128、isize/usizeInt整数String、str、str、mut StringStr(None)字符串boolBool布尔f32/f64Float浮点VecT、[T; N]、[T]List(BoxTyp)数组/切片其他具名类型如自定义 structResource(snake_case 名)按资源resource处理这一点在解析器的测试用例中有直接验证见 lib.rs 测试模块my_vec: Vecu8被识别为Typ::List(Box::new(Typ::Int))自定义类型CRes被识别为Typ::Resource(c_res)。也就是说Rust 脚本的参数可以直接引用 Windmill 资源类型平台会自动把资源对象反序列化后传入。另外如果脚本中没有main函数解析器会返回空参数签名并标记auto_kind: Some(lib)即视为纯库模块可被其他脚本 import这解释了为什么每个脚本必须导出 main。三、依赖声明脚本开头的内嵌 Cargo 清单Rust 脚本不像 Python 那样有requirements.txt单独文件而是在脚本文件开头的注释块内嵌入一份 Cargo 清单partial cargo.toml//! cargo //! [dependencies] //! anyhow 1.0.86 //! reqwest { version 0.11, features [json] } //! tokio { version 1, features [full] } //! use anyhow::anyhow; // ... rest of the code格式要点代码块必须使用doc comment//!包裹内部是 Markdown 围栏代码块语言标识为cargo内容以[dependencies]表开始逐行声明依赖与标准Cargo.toml语法完全一致支持版本号、features等完整特性serde 已被平台预置无需重复声明。解析器如何读取这段清单在 parse_rust_deps_into_manifest 中解析器依次尝试两种提取方式与rust-script、cargo-eval项目的逻辑一致见 find_embedded_manifest简写形式首行非空注释为// cargo-deps: dep1, dep2时按逗号拆分生成[dependencies]表未写版本号的依赖自动补*代码块形式从文档注释中用 Markdown 解析器pulldown-cmark抓取第一个cargo语言围栏代码块。随后解析器把用户声明的[dependencies]与默认清单合并。默认清单Cargo.toml.default如下[[bin]] name main path ./main.rs [package] authors [Anonymous] edition 2021 name main version 0.1.0 [profile.release] strip true [dependencies] serde { version 1.0.207, features [derive] } serde_json 1.0.124可见平台已为你处理好了edition 2021、[profile.release] strip true发布构建自动剥离符号减小产物体积以及serde/serde_json预置依赖——这正对应规范中Serde 无需再添加的说明。四、异步任务在同步 main 内创建 RuntimeWindmill 的 Rust 执行器要求main是同步函数。如果需要异步能力例如用reqwest发起 HTTP 请求规范做法是在main内部手动创建 tokio Runtime 并用block_on驱动//! cargo //! [dependencies] //! anyhow 1.0.86 //! tokio { version 1, features [full] } //! reqwest { version 0.11, features [json] } //! use anyhow::anyhow; use serde::Serialize; #[derive(Serialize, Debug)] struct Response { data: String, } fn main(url: String) - anyhow::ResultResponse { let rt tokio::runtime::Runtime::new()?; rt.block_on(async { let resp reqwest::get(url).await?.text().await?; Ok(Response { data: resp }) }) }关键点拆解tokio::runtime::Runtime::new()?创建多线程运行时?把创建失败传播为anyhow::Errorrt.block_on(...)同步阻塞当前线程直到异步闭包完成从而让main保持同步签名异步闭包内部可以正常使用.awaitreqwest的?错误reqwest::Error会被自动转换为anyhow::Error返回值Ok(Response {...})会经serde序列化为 JSON 输出。从执行器源码看这种同步外壳 内部 runtime的模式与 gen_cargo_crate 生成的包装代码完全兼容worker 生成的main.rs从args.json读取参数、调用你的main、把返回值写入result.json整个调用链是同步的因此任何异步逻辑都必须收敛在main内部完成。五、底层执行流程从源码到二进制缓存理解底层机制有助于排查编译慢、缓存失效等问题。Rust 脚本的一次运行在 worker 端经历以下阶段见 handle_rust_job计算缓存键compute_rust_hash对源码 依赖锁文件 关联模块计算哈希见 rust_cache_key并附加工作区注册表后缀查缓存若哈希对应的二进制已在本机缓存目录或对象存储中_rustbin/前缀直接软链接复用跳过编译生成 Cargo 工程gen_cargo_crate写入三份文件——合并后的Cargo.toml、读取args.json/写result.json的main.rs包装器、包含你业务代码与__WINDMILL_ARGS__参数结构体的inner.rs编译调用cargo build正式运行加--releasepreview 为 debug 构建启用沙箱时默认 nsjail编译在隔离环境中进行运行在 nsjail 沙箱或非沙箱的直接进程中执行编译产物/tmp/main注入环境变量与保留变量超时由 job 配置决定产出结果从result.json读取返回值同时缓存二进制供后续运行秒级复用。值得注意的两个工程细节构建目录策略get_build_dirrust_executor.rs在沙箱开启时为工作区路径创建者创建独立构建目录以兼顾缓存命中率与安全并用cargo sweep --maxsize默认 25GB可用CARGO_SWEEP_MAXSIZE环境变量调整异步清理膨胀的缓存Cargo registry 可配置通过 write_cargo_config 支持工作区级覆盖cargo_registries配置写入.cargo/config.toml便于私有镜像源场景。六、CLI 工作流编写、预览、同步与部署在本地用wmillCLI 开发 Rust 脚本时遵循 write-script-rust 技能文档 的约定可以避免为了测试而误部署的常见错误命令用途何时使用wmill script preview path直接运行本地文件不部署迭代本地脚本的默认选择有本地改动想试一下时用它wmill script run path运行工作区中已部署的版本仅在用户明确要测试部署版本、或没有本地改动时使用wmill generate-metadata重新生成本地.script.yaml输入 schema与.lock依赖锁并刷新wmill-lock.yaml内容哈希每次编辑脚本尤其是增删 import 或改动main参数后执行保证元数据与代码一致git push/wmill sync push将本地改动部署到工作区仅当用户明确要求部署/发布/推送时元数据同步是关键一步wmill-lock.yaml为每个条目记录内容哈希。修改脚本内容——尤其是增删 import 或改变main的参数——会使哈希失效导致.lock、.script.yaml输入 schema 与哈希行全部过期。此时应运行wmill generate-metadata可加路径参数收窄范围如wmill generate-metadata f/foo否则 git-sync 与 CI 中会出现虚假 diff。该命令只写本地文件、不部署但会重新解析依赖可能升级未固定版本的依赖与从 UI 部署行为一致因此运行后应 diff 一下.lock确认依赖版本没有意外跳动若改动面超出预期可先wmill generate-metadata --dry-run查看每个过期条目的原因content changed或depends on path。main函数参数在 Rust 中直接以fn main(param1: String, ...)形式声明preview 传参格式为wmill script preview path -d args。七、总结在 Windmill 中编写 Rust 脚本的要点可以浓缩为四句话写main同步函数、owned 参数、anyhow::ResultT返回、T可Serialize声明依赖在脚本顶部//!doc comment 内嵌cargo代码块serde 免声明做异步在同步main里创建 tokio Runtime 并block_on本地迭代编辑后先wmill script preview验证再wmill generate-metadata同步元数据最后才按用户意图git push/wmill sync push部署。通过 rust_executor.rs 与 windmill-parser-rust 的源码可以看到平台在简单脚本语法背后封装了完整的 Cargo 工程生成、签名解析、二进制缓存与沙箱执行链路——理解这条链路你就能更从容地写出既符合规范、又能在生产环境高效运行的 Rust 脚本。【免费下载链接】windmillOpen-source developer platform to power your entire infra and turn scripts into webhooks, workflows and UIs. Fastest workflow engine (13x vs Airflow). Open-source alternative to Retool and Temporal.项目地址: https://gitcode.com/GitHub_Trending/wi/windmill创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价