ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

用 Rust 编写 Bifrost WASM 插件:hello-world-wasm-rust 全流程实战

用 Rust 编写 Bifrost WASM 插件:hello-world-wasm-rust 全流程实战 人工智能LLM 网关API网关后端【免费下载链接】bifrostFastest enterprise AI gateway (50x faster than LiteLLM) with adaptive load balancer, cluster mode, guardrails, 1000 models support 100 µs overhead at 5k RPS.项目地址https://gitcode.com/gh_mirrors/bifrost31/bifrost点击查看免费下载Bifrost 允许通过插件在请求生命周期内拦截、改写、校验和短路请求/响应而 Rust 编写的插件可编译为 WebAssemblyWASM二进制实现跨平台、沙箱化、免版本匹配的动态扩展。本文以仓库中完整可运行的示例 examples/plugins/hello-world-wasm-rust 为骨架从环境准备、构建流程、WASM 导出契约、serde 数据结构到四个典型实战用例逐步拆解一个 Rust WASM 插件从源码到接入 Bifrost 配置的完整链路。读完本文你将掌握malloc/free内存契约、u64打包返回值格式、pre_hook/post_hook/http_intercept/http_stream_chunk_hook四种钩子的输入输出结构以及如何用cargo test保障插件质量。前置准备Rust 工具链与 WASM 目标编写 Rust WASM 插件前需要安装 Rust 工具链并添加wasm32-unknown-unknown编译目标。该目标与 wasi 目标不同它生成不依赖宿主操作系统的纯 WASM 模块正是 Bifrost 加载器所期望的形态# 安装 Rust若尚未安装 curl --proto https --tlsv1.2 -sSf https://sh.rustup.rs | sh # 添加 WASM 目标 rustup target add wasm32-unknown-unknown若要进一步压缩产物体积可安装wasm-opt来自 Binaryen 项目# macOS brew install binaryen # Linux apt install binaryen仓库的 Makefile 提供了make check-rust目标用于一键校验环境是否就绪它检查cargo是否可用并通过rustup target list --installed | grep wasm32-unknown-unknown验证 WASM 目标是否已安装随后输出rustc --version与目标确认信息。构建流程Makefile 与产物输出示例项目通过 Makefile 封装了全部构建逻辑包含四个常用目标# 构建 WASM 插件 make build # 使用 wasm-opt 优化构建需先安装 binaryen make build-optimized # 清理构建产物 make cleanmake build实际执行两条核心命令cargo build --release --target wasm32-unknown-unknown cp target/wasm32-unknown-unknown/release/hello_world_wasm_rust.wasm build/hello-world.wasm编译产物最终位于build/hello-world.wasm。make build-optimized会在常规构建基础上检测wasm-opt是否存在若存在则执行wasm-opt -Os -o build/hello-world.wasm build/hello-world.wasm-Os表示以优化体积为主若未安装则跳过优化并给出提示不会中断构建。Makefile 中还定义了PLUGIN_NAME hello-world、OUTPUT_DIR build、TARGET wasm32-unknown-unknown等变量make info可查看构建配置与当前产物状态。构建配置位于 Cargo.toml几处关键设置直接服务于 WASM 产物配置项值作用crate-type[cdylib]编译为 C 动态库形式导出#[no_mangle] pub extern C符号serde1.0derive特性提供序列化/反序列化派生宏serde_json1.0JSON 解析与生成profile.release.opt-levels以体积优先优化profile.release.ltotrue链接期优化削减冗余代码profile.release.striptrue剥离符号信息进一步缩小体积profile.release.panicabortpanic 直接中止而非 unwind避免引入 unwinding 运行时项目结构与模块划分示例项目遵循清晰的三文件结构详见 examples/plugins/hello-world-wasm-rustexamples/plugins/hello-world-wasm-rust/ ├── Cargo.toml # 工程清单与编译配置 ├── Makefile # 构建/优化/清理入口 └── src/ ├── lib.rs # 插件实现各导出钩子 ├── memory.rs # 内存管理工具malloc/free、字符串读写 └── types.rs # 类型定义镜像 Go SDK 结构lib.rs是插件的入口实现通过mod memory; mod types;引入另外两个模块memory.rs负责与宿主Bifrost 进程之间的内存交接types.rs则集中定义所有与 Go SDK 对应的数据结构保证 JSON 契约一致。WASM 导出契约插件必须实现的函数Bifrost 加载 WASM 插件时会按符号名调用插件导出的函数。所有导出函数必须使用#[no_mangle]与extern C声明避免符号名被 Rust 混淆。示例必须导出的函数如下导出函数签名说明malloc(size: u32) - u32为宿主分配内存供宿主写入数据free(ptr: u32, size: u32)释放宿主分配的内存Rust 的 dealloc 需要 sizeget_name() - u64返回插件名编码为打包的指针长度init(config_ptr, config_len: u32) - i32用配置初始化插件可选返回 0 表示成功http_intercept(input_ptr, input_len: u32) - u64HTTP 传输层拦截发生在请求进入 Bifrost 核心之前pre_hook(input_ptr, input_len: u32) - u64请求发送到 Provider 之前的钩子post_hook(input_ptr, input_len: u32) - u64收到 Provider 响应之后的钩子http_stream_chunk_hook(input_ptr, input_len: u32) - u64流式响应中每个 chunk 的逐块钩子位于 lib.rs 中cleanup() - i32清理资源返回 0 表示成功对照 docs/plugins/writing-wasm-plugin.mdx 中记录的 WASM 插件接口钩子集合与文档一致文档中的http_pre_hook/http_post_hook在本示例中对应合并为http_intercept与post_hook的职责划分并额外提供了流式 chunk 钩子。init 与配置存储lib.rs 中的init展示了配置注入模式宿主通过read_string(config_ptr, config_len)读取 JSON 配置字符串若为空则使用默认配置否则用serde_json::from_str解析为PluginConfig解析失败返回非零值1通知宿主。配置通过static mut PLUGIN_CONFIG: OptionPluginConfig全局存储cleanup时置回None。PluginConfig在 types.rs 中被定义为#[serde(flatten)]的HashMapString, serde_json::Value意味着任意自定义键值都会被接纳插件可自行扩展。返回值格式u64 打包指针与长度所有返回数据的导出函数使用u64打包格式传输 JSON 字符串高 32 位数据在 WASM 内存中的指针低 32 位数据长度对应的实现位于 memory.rs/// Pack a pointer and length into a single u64 /// Upper 32 bits: pointer, Lower 32 bits: length pub fn pack_result(ptr: u32, len: u32) - u64 { ((ptr as u64) 32) | (len as u64) }write_string先调用malloc分配内存再用std::ptr::copy_nonoverlapping将字节拷入最后打包返回read_string则通过slice::from_raw_parts按指针长度读取 UTF-8 字节并转换为String。宿主侧会先调用插件的malloc分配缓冲区写入 JSON 输入再调用钩子函数最后读取返回值指向的内存并调用free释放。数据类型镜像 Go SDK 的 serde 结构types.rs 定义了完整的插件数据结构所有结构体均派生Serialize, Deserialize, Default与 Go SDK 类型一一对应。其关键点在于容忍 Go JSON 编码器的 null 语义Go 对 nil 切片/映射会输出null而 serde 的#[serde(default)]只处理字段缺失、不处理显式 null因此 types.rs 内置了一个nullable模块提供string、string_map、i32_field、http_request、context等自定义反序列化器把null安全转换为默认值空字符串、空 HashMap、0 等。Context请求上下文#[derive(Debug, Clone, Serialize, Deserialize, Default)] #[serde(transparent)] pub struct BifrostContext(pub HashMapString, serde_json::Value); impl BifrostContext { pub fn new() - Self { Self(HashMap::new()) } pub fn set_value(mut self, key: str, value: impl Intoserde_json::Value); pub fn get(self, key: str) - Optionserde_json::Value; pub fn get_string(self, key: str) - Optionstr; pub fn get_bool(self, key: str) - Optionbool; pub fn get_i64(self, key: str) - Optioni64; pub fn contains_key(self, key: str) - bool; pub fn remove(mut self, key: str) - Optionserde_json::Value; pub fn inner(self) - HashMapString, serde_json::Value; pub fn inner_mut(mut self) - mut HashMapString, serde_json::Value; }Context 本质是一个map[string]any的动态键值容器request_id是宿主写入的常见键插件写入的自定义值会跨钩子持久传递例如pre_hook写入的值在post_hook中可见。HTTP 传输类型#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct HTTPRequest { pub method: String, pub path: String, pub headers: HashMapString, String, pub query: HashMapString, String, pub body: String, // base64 编码 } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct HTTPResponse { pub status_code: i32, pub headers: HashMapString, String, pub body: String, // base64 编码 }注意body为 base64 编码的字符串headers与query使用nullable::string_map反序列化以兼容 null 值。文档 docs/plugins/writing-wasm-plugin.mdx 特别提示request.headers和request.query保留客户端发送的原始大小写插件内查表时应做大小写不敏感比较如Content-Type/content-type/CONTENT-TYPE。Chat Completion 类型#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all lowercase)] pub enum ChatMessageRole { User, Assistant, System, Tool, Developer } #[derive(Debug, Clone, Serialize, Deserialize)] #[serde(untagged)] pub enum ChatMessageContent { Text(String), Blocks(VecChatContentBlock), } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ChatMessage { pub role: ChatMessageRole, pub content: OptionChatMessageContent, pub name: OptionString, pub tool_call_id: OptionString, pub tool_calls: OptionVecToolCall, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ChatParameters { pub temperature: Optionf64, pub max_completion_tokens: Optioni32, pub top_p: Optionf64, pub frequency_penalty: Optionf64, pub presence_penalty: Optionf64, pub stop: OptionVecString, pub tools: OptionVecChatTool, #[serde(flatten)] pub extra: HashMapString, serde_json::Value, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct BifrostChatRequest { pub provider: String, pub model: String, pub input: VecChatMessage, pub params: OptionChatParameters, pub fallbacks: OptionVecFallback, }ChatMessageRole用rename_all lowercase让枚举值序列化为小写 JSONuser、assistant等ChatMessageContent用untagged支持字符串与内容块数组两种形态ChatParameters通过#[serde(flatten)]的extra兜底接收未知参数。响应类型#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct LLMUsage { pub prompt_tokens: i32, pub completion_tokens: i32, pub total_tokens: i32, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ResponseChoice { pub index: i32, pub message: OptionChatMessage, pub delta: OptionChatMessage, pub finish_reason: OptionString, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct BifrostChatResponse { pub id: String, pub model: String, pub choices: VecResponseChoice, pub usage: OptionLLMUsage, pub created: Optioni64, pub object: OptionString, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct BifrostResponse { pub chat_response: OptionBifrostChatResponse, }错误类型与短路结构#[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct ErrorField { pub message: String, #[serde(rename type)] pub error_type: OptionString, pub code: OptionString, } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct BifrostError { pub error: ErrorField, pub status_code: Optioni32, pub allow_fallbacks: Optionbool, } impl BifrostError { pub fn new(message: str) - Self; pub fn with_type(self, error_type: str) - Self; pub fn with_code(self, code: str) - Self; pub fn with_status(self, status: i32) - Self; } #[derive(Debug, Clone, Serialize, Deserialize, Default)] pub struct LLMPluginShortCircuit { pub response: OptionBifrostResponse, pub error: OptionBifrostError, }BifrostError的构建器模式new→with_type→with_code→with_status让插件可以流畅地构造标准错误响应allow_fallbacks字段控制是否允许 Bifrost 在错误发生后继续尝试配置的 fallback 模型。钩子输入/输出结构三个核心 JSON 契约插件与宿主之间的全部复杂数据都以 JSON 字符串交换因此掌握每个钩子的输入输出结构是编写正确插件的前提。以下三组结构直接取自 README 与 docs/plugins/writing-wasm-plugin.mdx 的契约定义。http_intercept输入含context与传输层request{ context: { request_id: abc-123 }, request: { method: POST, path: /v1/chat/completions, headers: { Content-Type: application/json }, query: {}, body: base64-encoded } }输出has_response: false表示放行置true并填充response即可直接以 HTTP 响应短路{ context: { request_id: abc-123 }, request: {}, response: { status_code: 200, headers: {}, body: base64 }, has_response: false, error: }示例 lib.rs 中的http_intercept展示了两个细节一是用extract_column从 serde 错误信息中提取列号截取输入串中出错位置前后各 50 字符拼入错误上下文方便调试 JSON 解析失败二是向 context 写入from-http标记后透传请求request保留原值。pre_hook输入request是 Bifrost 统一请求结构{ context: { request_id: abc-123 }, request: { provider: openai, model: gpt-4, input: [{ role: user, content: Hello }], params: { temperature: 0.7 } } }输出has_short_circuit: true并携带short_circuit即可短路{ context: { request_id: abc-123, plugin_processed: true }, request: {}, short_circuit: { response: { chat_response: { ... } } }, has_short_circuit: false, error: }types.rs 中PreHookInput把request保存为通用serde_json::Value并提供parse_request()与get_provider_model()两个便捷方法前者尝试把request解析为BifrostRequest后者在解析失败时退化为直接从 JSON 顶层读取provider/model字段兼顾了完整结构与简化结构两种宿主输入。post_hook输入同时携带响应与错误has_error标记出错状态{ context: { request_id: abc-123, plugin_processed: true }, response: { chat_response: { id: chatcmpl-123, model: gpt-4, choices: [{ index: 0, message: { role: assistant, content: Hi! } }], usage: { prompt_tokens: 5, completion_tokens: 10, total_tokens: 15 } } }, error: {}, has_error: false }输出hook_error用于报告插件自身的处理错误与上游错误区分{ context: { request_id: abc-123, post_hook_completed: true }, response: {}, error: {}, has_error: false, hook_error: }PostHookInput提供parse_response()解析为BifrostResponse与parse_error()仅在has_error时解析为BifrostError供插件按需改写。四个实战用例从透传到短路以下用例与 README 及 lib.rs 中的实现一一对应覆盖插件最常见的四类诉求。用例一修改 Context 传递自定义值在pre_hook中向 context 写入自定义标记后续钩子即可读取#[no_mangle] pub extern C fn pre_hook(input_ptr: u32, input_len: u32) - u64 { let input_str read_string(input_ptr, input_len); let input: PreHookInput serde_json::from_str(input_str).unwrap(); let mut output PreHookOutput { context: input.context.clone(), ..Default::default() }; // Add custom values to context output.context.set_value(plugin_processed, serde_json::json!(true)); output.context.set_value(plugin_name, serde_json::json!(my-rust-plugin)); write_string(serde_json::to_string(output).unwrap()) }用例二短路并返回 Mock 响应当命中特定模型如mock-model时插件直接构造BifrostChatResponse并设置has_short_circuit: true请求不会发往任何 Provider#[no_mangle] pub extern C fn pre_hook(input_ptr: u32, input_len: u32) - u64 { let input_str read_string(input_ptr, input_len); let input: PreHookInput serde_json::from_str(input_str).unwrap(); let (provider, model) input.get_provider_model(); if model mock-model { let mut output PreHookOutput { context: input.context.clone(), has_short_circuit: true, ..Default::default() }; let mock_response BifrostResponse { chat_response: Some(BifrostChatResponse { id: format!(mock-{}, input.context.request_id.unwrap_or_default()), model: mock-model.to_string(), choices: vec![ResponseChoice { index: 0, message: Some(ChatMessage { role: ChatMessageRole::Assistant, content: Some(ChatMessageContent::Text( This is a mock response!.to_string() )), ..Default::default() }), finish_reason: Some(stop.to_string()), ..Default::default() }], usage: Some(LLMUsage { prompt_tokens: 10, completion_tokens: 15, total_tokens: 25, ..Default::default() }), ..Default::default() }), ..Default::default() }; output.short_circuit Some(LLMPluginShortCircuit { response: Some(mock_response), error: None, }); return write_string(serde_json::to_string(output).unwrap()); } // Pass through let output PreHookOutput { context: input.context, ..Default::default() }; write_string(serde_json::to_string(output).unwrap()) }用例三短路并返回错误限流场景当触发限流等条件时插件构造BifrostError配合with_status(429)让 Bifrost 向客户端返回 429 响应#[no_mangle] pub extern C fn pre_hook(input_ptr: u32, input_len: u32) - u64 { let input_str read_string(input_ptr, input_len); let input: PreHookInput serde_json::from_str(input_str).unwrap(); if should_rate_limit(input.context) { let mut output PreHookOutput { context: input.context.clone(), has_short_circuit: true, ..Default::default() }; output.short_circuit Some(LLMPluginShortCircuit { response: None, error: Some( BifrostError::new(Rate limit exceeded) .with_type(rate_limit) .with_code(429) .with_status(429) ), }); return write_string(serde_json::to_string(output).unwrap()); } // Pass through let output PreHookOutput { context: input.context, ..Default::default() }; write_string(serde_json::to_string(output).unwrap()) }should_rate_limit是示例中预留的实现占位函数默认返回false读者可在此接入自己的限流逻辑。用例四post_hook 中改写响应post_hook既处理错误也处理成功响应。示例展示了在错误信息末尾追加插件标记、以及在成功响应的模型名上追加(via rust-wasm)后缀#[no_mangle] pub extern C fn post_hook(input_ptr: u32, input_len: u32) - u64 { let input_str read_string(input_ptr, input_len); let input: PostHookInput serde_json::from_str(input_str).unwrap(); let mut output PostHookOutput { context: input.context.clone(), ..Default::default() }; // Handle errors if input.has_error { output.has_error true; output.error input.error.clone(); if let Some(mut error) input.parse_error() { error.error.message format!({} (via rust plugin), error.error.message); output.error serde_json::to_value(error).unwrap_or_default(); } return write_string(serde_json::to_string(output).unwrap()); } // Pass through or modify response if let Some(mut response) input.parse_response() { if let Some(ref mut chat) response.chat_response { chat.model format!({} (via rust-wasm), chat.model); } output.response serde_json::to_value(response).unwrap_or_default(); } write_string(serde_json::to_string(output).unwrap()) }注意当钩子无法解析输入时正确做法是把错误写入输出结构体如error或hook_error后照常返回打包结果而不是让 WASM panic 或返回空指针这样宿主可以把插件错误安全地纳入请求流程。流式响应钩子http_stream_chunk_hook除上述三个钩子外示例还实现了http_stream_chunk_hook它在流式响应的每个 chunk 被写回客户端之前逐块调用支持修改、跳过或终止流。输入输出结构为pub struct HTTPStreamChunkHookInput { pub context: BifrostContext, pub request: serde_json::Value, // BifrostRequest as JSON pub chunk: serde_json::Value, // BifrostStreamChunk as JSON } pub struct HTTPStreamChunkHookOutput { pub context: BifrostContext, pub chunk: Optionserde_json::Value, // None 表示跳过该块 pub has_chunk: bool, pub skip: bool, pub error: String, }chunk字段包含序列化为 JSON 的BifrostStreamChunk其具体内容取决于当前响应类型聊天补全流式块形如{id:...,object:chat.completion.chunk,choices:[...],model:...}文本补全、Responses API、语音/转写/图像流式响应则对应各自的字段出错时是{error:{...}}。该字段不包含 SSE 帧包装没有data:前缀和\n\n后缀。示例实现默认透传 chunkhas_chunk: true, skip: false同时写入from-stream-chunk上下文标记见 lib.rs。接入 Bifrostplugins 配置编译出hello-world.wasm后在 Bifrost 的config.json中通过plugins数组加载。通用字段如下完整说明见 docs/deployment-guides/config-json/plugins.mdx字段类型必填说明namestring是插件名称enabledboolean是是否启用configobject视插件而定插件专属配置pathstring否自定义插件二进制或 WASM 文件路径placementstring否仅 DB 模式执行位置pre_builtin/builtin/post_builtinorderinteger否仅 DB 模式组内执行顺序数值小者先执行针对本示例的配置{ plugins: [ { path: /path/to/hello-world.wasm, name: hello-world-wasm-rust, enabled: true, config: { custom_option: value } } ] }config中的内容会以 JSON 字符串形式传入插件的init由PluginConfig接收。自定义插件在plugins数组中被归类为Custom / Dynamic Plugins与semantic_cache、otel、maxim等显式启用的内置插件并列telemetry、logging、governance 则是自动加载的内置插件不需要写入plugins数组。关于执行顺序docs/plugins/sequencing.mdx 说明 Bifrost 将插件划分为pre_builtin、builtin、post_builtin三个组组间固定顺序执行组内按order升序响应侧的 post-hook 按 LIFO 逆序执行因此先执行的插件其响应钩子最后执行编写有依赖关系的插件时需留意这一行为。测试与质量保障插件自带单元测试使用标准 Rust 测试命令运行cargo test测试全部定义在 types.rs 的#[cfg(test)] mod tests中覆盖了插件最容易出错的边界Context 序列化/反序列化验证set_value/get_string/get_bool/get_i64/contains_key/remove等方法的正确性ChatMessage 与 BifrostError 序列化验证枚举小写化与with_type等构建器产出pre_hook 输入解析用 JSON 反序列化PreHookInput并断言get_provider_model()返回正确的 provider/modelnull 字段兼容模拟 Go 侧输出null如headers: null、status_code: null验证nullable模块能正确降级为默认值——这是 WASM 插件跨语言互操作中最常见的坑。优势与边界选择 Rust WASM 编写 Bifrost 插件对照 README 的 Benefits 列表与 docs/plugins/writing-wasm-plugin.mdx 的说明性能Rust 编译为高度优化的 WASM热路径开销低安全性无 GC 的内存安全模型WASM 提供沙箱化隔离执行小体积Rust WASM 产物通常非常小配合wasm-opt -Os可进一步压缩跨平台单个.wasm二进制可在任意操作系统/架构上运行无需像 Go.so插件那样匹配编译平台与 Go 版本详见 docs/plugins/getting-started.mdx 的平台限制说明类型安全serde 派生宏提供强类型 JSON 编解码nullable模块弥合了 Go 与 Rust 的 JSON 语义差异健壮的 JSON 处理serde_json 提供完备的解析、错误定位与任意值Value支持。需要明确的两点边界单向契约WASM 插件接口是单向的——宿主调用导出函数并传入 JSON插件返回 JSON不存在可回调宿主的方法。因此 Go 原生插件可用的ctx.GetModelInfo、ctx.CalculateCost等上下文访问器在 WASM 中不可用插件只能看到钩子输入里携带的数据。需要模型定价或能力查询时应改用 Go 插件。弃用状态根据 docs/plugins/writing-wasm-plugin.mdx 顶部的弃用警告WASM 自定义插件已标记为Deprecated现有 WASM 插件仍可继续运行但新插件开发应优先使用原生 Go 插件官方正在推进基于 webhook 的跨语言扩展路径。本示例的价值在于完整展示 WASM 插件的契约、数据结构与边界能力可作为既有部署维护与迁移的参考蓝本。参考资源示例完整源码examples/plugins/hello-world-wasm-rust含 README、lib.rs、types.rs、memory.rs、Makefile、Cargo.tomlWASM 插件官方指南含 TypeScript/TinyGo/Rust 三语言对照与完整 JSON 契约docs/plugins/writing-wasm-plugin.mdx插件快速入门与 Go 插件说明docs/plugins/getting-started.mdx、docs/plugins/writing-go-plugin.mdxplugins 数组配置参考docs/deployment-guides/config-json/plugins.mdx插件执行顺序docs/plugins/sequencing.mdx同目录其他语言对照示例examples/plugins/hello-world-wasm-go、examples/plugins/hello-world-wasm-typescript赞分享人工智能LLM 网关API网关后端【免费下载链接】bifrostFastest enterprise AI gateway (50x faster than LiteLLM) with adaptive load balancer, cluster mode, guardrails, 1000 models support 100 µs overhead at 5k RPS.项目地址https://gitcode.com/gh_mirrors/bifrost31/bifrost点击查看免费下载相关推荐Druid Web 版 Hello World用 Rust wasm-pack 构建浏览器端 UI 应用全流程指南Druid Web 版 Hello World用 Rust wasm pack 构建浏览器端 UI 应用全流程指南 导读 druid/examples/h跨平台桌面应用UI组件Apache ShenYu Wasm 数据同步插件实战将 Rust 编写的 PluginDataHandler 编译为 wasm 并与 Java 侧集成Apache ShenYu Wasm 数据同步插件实战将 Rust 编写的 PluginDataHandler 编译为 wasm 并与 Java 侧集成 本文后端API网关微服务在 RIOT 中用 Rust 编写 IoT 应用rust-hello-world 示例全解析在 RIOT 中用 Rust 编写 IoT 应用rust hello world 示例全解析 导读 RIOT 是一个面向 IoT 的友好操作系统除了经典的物联网嵌入式操作系统实时系统上一篇Windows 11 LTSC 24H2 终极指南5分钟为精简版系统添加Microsoft Store应用商店下一篇终极指南在PC上免费体验Switch游戏的yuzu模拟器完全配置方案创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表