9.2 写一个 Rust 节点
上一节说好了——本课学 Rust 只求"看得懂、能照着改"。这一节我们就动手写第一个 Rust 节点。
好消息:DORA 节点不管什么语言,骨架都一样。你在第四章烂熟的"连线 → 事件循环 → 收发"三段式,在 Rust 里原样成立。你只是在学"同一件事的 Rust 说法"。
别被 Rust 吓到!我偷偷告诉你:它和 Python 节点长得像双胞胎,只是"穿的衣服"不一样。你已经会 Python 版了,这节就是认一下它的 Rust 打扮~
学习目标
学完本节,你将能够:
- 看懂一个 Rust DORA 节点的完整结构;
- 把 Rust 节点的每一部分和 Python 版一一对应起来;
- 照着模板改出一个自己的 Rust 节点;
- 知道
Cargo.toml里要写什么依赖、怎么编译。
前置要求
- 完成 9.1 何时用 Rust;
- 记得第四章的 Python 三段式(节点生命周期)——本节全程和它对照。
先看一眼:Rust 和 Python 的骨架对照
先不解释语法,直接把两种语言的最小节点放一起,你感受一下"像不像":
看出来了吗?结构完全一样:
你要学的不是"新概念",只是"新写法"。 下面逐块拆开。
完整示例:一个随机数发送节点
我们照着 DORA 官方示例,写一个"每次 tick 就发一个随机数"的节点。目录结构(Rust 项目比 Python 多几个文件,别慌,下面都会解释):
src/main.rs
逐块讲解(对照 Python)
开头的 use——相当于 Python 的 import:
use dora_node_api::{...}= Python 的from dora import Node,把要用的东西引进来;Event是"事件"类型,IntoArrow是"转成 Arrow 格式"的工具,DataId是"输出名字"的类型;eyre::Result用于错误处理,暂时把它当"固定写法"记住。
① 连线:
- 对应 Python 的
node = Node(); - 区别:Rust 一次给你两样东西——
node(用来发数据)和events(事件流,用来收数据)。Python 里这两者都藏在node一个对象里,Rust 把它们分开了。 let相当于定义变量;mut表示"这个变量后面会变"(Rust 要求你明确说明);- 结尾的
?意思是"如果这一步出错了,就停下来报错"——是 Rust 处理错误的简洁写法。
② 主循环:
- 对应 Python 的
for event in node:; events.recv()取下一个事件;while let Some(event) = ...的意思是"只要还能取到事件,就继续循环"。事件取完了(None)就自动结束。
③ 判断事件类型——match:
match是 Rust 的"分类处理",比 Python 的if/elif更强大也更整齐;Event::Input { id, metadata, .. }对应 Python 判断event["type"] == "INPUT",同时把id、metadata取出来备用(..表示"其余的先不管");Event::Stop(_)对应event["type"] == "STOP";_ => {}是"其它情况都忽略",相当于兜底的else: pass。
发送输出:
- 对应 Python 的
node.send_output("random", data); - 三个参数:输出名、元数据、数据;
random.into_arrow()= Python 的pa.array([random])——把普通数字转成 Arrow 格式(还记得第五章吗,黑板上只认 Arrow);output.clone()的.clone()是 Rust 特有的"复制一份",因为循环里要反复用这个名字。暂时当固定写法。
看到 into_arrow() 了吗?和 Python 的 pa.array 是一回事——都是"把数据翻译成黑板认的字"。Rust 同学和 Python 同学,写的是同一块黑板!
Cargo.toml:Rust 项目的"说明书"
Python 脚本可以直接跑,但 Rust 是编译型语言,需要一份"项目说明书"告诉编译器:这个项目叫什么、要用哪些外部库。这份说明书就是 Cargo.toml:
[package]段:项目的基本信息;[dependencies]段:要用的外部库,相当于 Python 的pip install列表;dora-node-api就是 Rust 版的"DORA 库",对应 Python 的from dora import Node。
本课锁定 DORA 1.0 RC。你可以使用国内镜像加速 Rust 编译(见第二章)。
编译并运行
Rust 节点在跑之前要先编译(把源码变成可执行程序)。DORA 的做法是在 dataflow.yml 里写一条 build 指令,dora build 时自动编译。
一个最小的 dataflow.yml:
build: cargo build:cargo是 Rust 的官方工具(相当于 Python 的 pip+运行器合体),cargo build把源码编译成可执行文件;path: target/debug/my-rust-node:编译好的程序默认放在target/debug/下,名字就是Cargo.toml里的name;- 其余的
inputs/outputs和 Python 节点完全一样。
然后照常两步走:
Rust 首次编译要下载并编译一堆依赖,可能要等几分钟,这是正常的。编译产物会被缓存,之后再 build 就快了。Python 节点没有这一步,因为它不用编译——这也是 Python"随写随跑"的便利之处。
动手练习
照着上面的模板改一个节点:它有一个输入 value,每次收到一个数字,就把它乘以 2 再从 doubled 输出发出去。(对应你在第五章写过的 Python 加倍节点。)
提示:
- 读输入数据需要把
data转成具体的数字类型,这一步稍有 Rust 门槛; - 如果卡在"怎么读数据",没关系——下一节和小项目会给你完整可运行的读输入代码。这里能改出"发送"部分就算达标。
参考答案(发送部分骨架)
核心是把 tick 分支换成 value 分支,读入数字后 ×2 再发。读取 Arrow 数据的完整写法(downcast 成具体数组类型)会在小项目⑥里给出可直接运行的版本:
现阶段你能看懂这个结构、知道"改哪里",就完全达到了本课"最小够用"的目标。
常见报错 FAQ
error: linker not found 或编译工具缺失
Rust 编译需要系统的 C 链接器等工具。若报此错,请安装所在平台的构建工具链(macOS: xcode-select --install, Linux: build-essential)。
cannot find function ... in crate
多半是 Cargo.toml 里的依赖版本对不上,或 use 语句写错。确认 dora-node-api = "1.0.0-rc1",并对照示例的 use 行。
main.rs 但运行的还是旧行为
Rust 要重新编译才生效。改完代码记得重新 dora build(或 cargo build),再 dora run。这和 Python"改完直接跑"不同——编译型语言的必经步骤。
小结
- Rust 节点和 Python 节点是同一套三段式:
init_from_env(连线)→while let ... events.recv()(循环)→match event(判类型)→send_output(收发)。 - Rust 用
use引库(≈import)、用match分类事件(≈if/elif)、用into_arrow()转 Arrow(≈pa.array)。 - Rust 是编译型语言:需要
Cargo.toml说明依赖,并在dataflow.yml里用build: cargo build先编译再运行。 - 你不用精通 Rust,看懂结构、能照着改就达到了本课目标。
下一节,我们让 Rust 节点和 Python 节点在同一条数据流里协作——这才是混合编程真正的威力。