团队Rust WASM编码规范 V1.0

文档版本: V1.0(初稿)

制定日期: 2026-02-28

制定人: 林栖(cat07)

适用范围: 所有采用Rust WebAssembly + Vue套壳前端技术栈的工程

1. 核心设计理念

1.1 架构定位

1.2 核心原则

1.2.1 业务异常封闭原则

JavaScript只是渲染套壳,所有业务异常在Rust-WASM内部处理完毕,不向JavaScript传递。

1.2.2 统一错误处理原则

1.2.3 用户感知反馈原则

1.2.4 安全可靠原则

2. 必须遵守的规则

2.1 `#[wasm_bindgen]`函数签名规则

规则1:严禁返回`Result<..., JsValue>`


// ✅ 正确:内部处理所有错误
#[wasm_bindgen]
pub fn handle_click() {
    match business_logic() {
        Ok(result) => { let _ = JS_FNS.set_data_obj("result", Some(&result)); }
        Err(e) => { let _ = JS_FNS.alert(&format!("失败: {}", e)); }
    }
}

// ❌ 错误:业务异常泄漏到JavaScript
#[wasm_bindgen]
pub fn handle_click() -> Result<(), JsValue> {
    business_logic()?;  // 错误会传到JavaScript
    Ok(())
}

规则2:必须无返回值或只返回`JsValue`(纯数据)


// ✅ 正确:返回纯数据
#[wasm_bindgen]
pub fn get_config() -> JsValue {
    let config = Config { /* ... */ };
    serde_wasm_bindgen::to_value(&config).unwrap_or(JsValue::null())
}

// ✅ 正确:无返回值
#[wasm_bindgen]
pub fn save_data(data: &str) {
    match parse_and_save(data) {
        Ok(()) => { let _ = JS_FNS.alert("保存成功"); }
        Err(e) => { let _ = JS_FNS.alert(&format!("保存失败: {}", e)); }
    }
}

规则3:异步函数必须内部处理错误


// ✅ 正确:async函数内部处理错误
#[wasm_bindgen]
pub async fn fetch_data(url: String) {
    match reqwest::get(&url).await {
        Ok(response) => {
            match response.text().await {
                Ok(text) => { let _ = JS_FNS.set_data_str("data", &text); }
                Err(e) => { let _ = JS_FNS.alert(&format!("读取响应失败: {}", e)); }
            }
        }
        Err(e) => { let _ = JS_FNS.alert(&format!("网络请求失败: {}", e)); }
    }
}

2.2 错误类型统一规则

规则4:所有错误统一为`CatError`


// ✅ 正确:第三方错误立即转换
external_lib::call()
    .map_err(|e| CatError::new_with(format!("外部错误: {}", e), -5000))?;

// ✅ 正确:Result类型统一转换
fn internal_logic() -> CatResult {
    let data = some_operation()?;  // 自动转换为CatError
    Ok(data)
}

规则5:JavaScript异常通过`JS_FNS`自动转换


// ✅ 正确:JS_FNS调用内部已处理JavaScript异常
JS_FNS.set_data_str("key", "value")?;  // 内部已转CatError
JS_FNS.alert("消息")?;
JS_FNS.set_data_obj("data", Some(&data))?;

2.3 用户反馈规则

规则6:所有错误必须提供用户可感知的反馈


// ✅ 正确:错误分支都有用户反馈
match api_call().await {
    Ok(data) => { 
        let _ = JS_FNS.set_data_obj("data", Some(&data));
        let _ = JS_FNS.alert("操作成功");
    }
    Err(e) => { 
        let _ = JS_FNS.alert(&format!("操作失败: {}", e));
        let _ = JS_FNS.set_data_str("error", &e.to_string());
    }
}

规则7:使用适当的反馈方式

2.4 Panic安全规则

规则8:避免`unwrap()`和`expect()`


// ❌ 错误:可能导致panic
let value = serde_wasm_bindgen::to_value(&data).unwrap();
let value = vec[index];  // 直接索引可能panic
self.quit_login().expect("Quit Login Fail");

// ✅ 正确:安全访问模式
match serde_wasm_bindgen::to_value(&data) {
    Ok(value) => { /* 使用value */ }
    Err(e) => { 
        let _ = JS_FNS.alert(&format!("数据序列化失败: {}", e));
        return;
    }
}

let value = vec.get(index).ok_or(CatError::new("索引越界"))?;

if let Err(e) = self.quit_login() {
    let _ = JS_FNS.alert(&format!("退出登录失败: {}", e));
}

规则9:严禁使用`panic::catch_unwind`包装`JS_FNS`调用


// ❌ 错误:不应使用catch_unwind包装JavaScript调用
let _ = panic::catch_unwind(|| {
    JS_FNS.alert("消息");
});

// ✅ 正确:JS_FNS内部已处理异常
let _ = JS_FNS.alert("消息");

规则10:必须使用安全访问模式


// ✅ 正确:使用安全访问方法
let value = vec.get(index).ok_or(CatError::new("索引越界"))?;
let value = map.get(key).ok_or(CatError::new("键不存在"))?;
let value = optional_value.ok_or(CatError::new("值缺失"))?;

3. 团队特有编码约定

3.1 `JS_FNS`抽象层约定

约定1:所有JavaScript交互必须通过`JS_FNS`


// ✅ 正确:通过JS_FNS抽象层
let _ = JS_FNS.set_data_str("key", "value");
let _ = JS_FNS.alert("消息");
let _ = JS_FNS.set_data_obj("data", Some(&data));

// ❌ 错误:直接调用JavaScript
#[wasm_bindgen]
extern "C" {
    fn alert(message: &str);
}
alert("消息");

约定2:`JS_FNS`调用忽略返回值使用`let _ =`


// ✅ 正确:忽略返回值,避免未使用警告
let _ = JS_FNS.alert("操作成功");

// ❌ 避免:产生未使用返回值警告
JS_FNS.alert("操作成功");

3.2 异步处理约定

约定3:使用`wasm_bindgen_futures`进行异步操作


// ✅ 正确:使用标准异步模式
use wasm_bindgen_futures;

#[wasm_bindgen]
pub async fn async_operation() {
    // 异步逻辑,内部处理所有错误
}

约定4:异步链式调用保持错误封闭


// ✅ 正确:异步链内部处理错误
async fn complex_operation() -> CatResult {
    let step1 = api_call1().await?;
    let step2 = process_data(step1).await?;
    let step3 = api_call2(step2).await?;
    Ok(step3)
}

#[wasm_bindgen]
pub async fn perform_complex_operation() {
    match complex_operation().await {
        Ok(result) => { let _ = JS_FNS.set_data_obj("result", Some(&result)); }
        Err(e) => { let _ = JS_FNS.alert(&format!("操作失败: {}", e)); }
    }
}

3.3 状态管理约定

约定5:全局状态使用`Arc>`


// ✅ 正确:全局状态管理
lazy_static! {
    static ref GLOBAL_STATE: Arc> = 
        Arc::new(Mutex::new(AppState::default()));
}

// 安全访问全局状态
fn update_state() -> CatResult<()> {
    let mut state = GLOBAL_STATE.lock()
        .map_err(|_| CatError::new("状态锁获取失败"))?;
    state.some_field = new_value;
    Ok(())
}

约定6:避免锁操作中的`unwrap()`


// ❌ 错误:锁操作后使用unwrap
let state = GLOBAL_STATE.lock().unwrap();

// ✅ 正确:处理可能的锁错误
let state = GLOBAL_STATE.lock()
    .map_err(|_| CatError::new("状态锁获取失败"))?;

3.4 前端通信约定

约定7:通过`setData()`接口更新前端


// ✅ 正确:使用统一的setData接口
let _ = JS_FNS.set_data_str("loading", "true");
let _ = JS_FNS.set_data_obj("user_data", Some(&user));
let _ = JS_FNS.set_data_bool("is_authenticated", true);

约定8:数据类型与前端Vue属性匹配

3.5 日志记录约定

约定9:使用统一的日志级别


// ✅ 正确:根据场景使用适当日志级别
error!("严重错误: {}", e);      // 需要立即关注的错误
warn!("警告信息: {}", warning);  // 潜在问题
info!("业务日志: {}", info);     // 正常业务流程
debug!("调试信息: {}", data);    // 调试信息
trace!("详细追踪: {}", detail);  // 详细追踪

约定10:WASM环境日志配置


// ✅ 正确:根据环境配置日志
if pub_consts::DEBUG_MODE {
    wasm_logger::init(wasm_logger::Config::default());
} else {
    wasm_logger::init(wasm_logger::Config::new(log::Level::Info));
}

3.6 浏览器API封装约定

约定11:使用`browser_page_plugin`封装web_sys API

当业务确保WASM在浏览器常规环境下访问使用时,应使用browserpageplugin模块封装的浏览器API,避免直接使用web_sys原始API。


// ✅ 正确:使用封装后的浏览器API
use crate::js_impl::wasm_utils::{WasmWindow, WasmLocation};

fn business_logic() -> CatResult<()> {
    let window = WasmWindow::try_default()?;  // 返回CatResult
    let location = window.location();
    let href = location.href()?;  // 返回CatResult
    
    // 使用?操作符简化错误处理
    let task_id = location.find_query_param("taskId")?;
    Ok(())
}

// ❌ 错误:直接使用web_sys,需要重复错误处理样板代码
use web_sys::{window, Location};

fn business_logic() -> Result<(), CatError> {
    let win = window().ok_or(CatError::new("window is None"))?;
    let location = win.location();
    let href = location.href().map_err(|e| CatError::new_with(format!("href error: {:?}", e), -5))?;
    // ... 更多样板代码
}

约定12:封装API返回`CatResult`以便链式调用

所有浏览器API封装应返回CatResult类型,支持?操作符,简化错误处理。


// ✅ 正确:封装方法返回CatResult,支持链式调用
pub struct WasmLocation {
    pub org: Location,
}

impl WasmLocation {
    pub fn href(&self) -> Result {
        match self.org.href() {
            Ok(href) => Ok(href),
            Err(e) => Err(CatError::new_with(format!("href error: {:?}", e), -5)),
        }
    }
    
    pub fn set_href>(&self, url: T) -> Result<(), CatError> {
        match self.org.set_href(url.as_ref()) {
            Ok(_) => Ok(()),
            Err(e) => Err(CatError::new_with(format!("{:?}", e), -5)),
        }
    }
}

约定13:在业务逻辑层使用`?`传播错误,在WASM接口层统一处理


// ✅ 正确:分层错误处理
// 业务逻辑层 - 返回CatResult,使用?传播错误
fn process_task() -> CatResult {
    let window = WasmWindow::try_default()?;
    let location = window.location();
    let task_id = location.find_query_param("taskId")?;
    let data = fetch_task_data(&task_id).await?;
    Ok(data)
}

// WASM接口层 - 内部处理错误,提供用户反馈
#[wasm_bindgen]
pub async fn handle_task() {
    match process_task().await {
        Ok(data) => {
            let _ = JS_FNS.set_data_obj("task_data", Some(&data));
            let _ = JS_FNS.alert("任务处理成功");
        }
        Err(e) => {
            let _ = JS_FNS.alert(&format!("任务处理失败: {}", e));
            let _ = JS_FNS.set_data_str("error", &e.to_string());
        }
    }
}

4. 架构约束

4.1 模块组织约束

约束1:WASM模块分层架构


utaai-urw-wasm/src/
├── lib.rs                    # WASM入口,#[wasm_bindgen]函数定义
├── client/                   # API客户端层
├── js_impl/                  # JavaScript交互抽象层
│   ├── mod.rs               # JS_FNS实现
│   └── convert/             # 数据类型转换
├── workflow_state/          # 工作流状态管理
├── generation_controller/   # 生成控制层
└── events.rs                # 事件处理层

约束2:错误处理分层架构

  1. **底层操作层**:返回`CatResult`,使用`CatError`
  2. **业务逻辑层**:组合底层操作,返回`CatResult`
  3. **WASM接口层**:`#[wasm_bindgen]`函数,内部处理所有错误,提供用户反馈

4.2 代码审查检查清单

4.2.1 函数签名检查

4.2.2 错误处理检查

4.2.3 安全访问检查

4.2.4 用户反馈检查

4.2.5 代码风格检查

4.3 常见错误模式与修复

错误模式1:`Result<(), JsValue>`返回类型

问题:业务异常泄漏到JavaScript层

修复:改为无返回值函数,内部处理错误并提供用户反馈

错误模式2:`unwrap()`滥用

问题:可能导致运行时panic,WASM模块崩溃

修复:使用matchok_or()进行安全访问

错误模式3:缺少用户反馈

问题:错误发生时用户无感知,体验差

修复:所有错误分支添加JS_FNS.alert()或状态更新

错误模式4:直接JavaScript交互

问题:绕过JS_FNS抽象层,错误处理不一致

修复:统一通过JS_FNS进行JavaScript交互

5. 实施与验证

5.1 代码审查工具


# 检查违规模式
grep -r "Result<.*JsValue>" src/
grep -r "\.unwrap()" src/
grep -r "\.expect(" src/
grep -r "panic::catch_unwind" src/
grep -r "map_err.*JsValue" src/

# 检查合规函数签名
grep -r "#\[wasm_bindgen\].*->" src/

5.2 构建验证


# 确保WASM构建通过
cargo check --target wasm32-unknown-unknown

# 确保无编译警告
cargo build --target wasm32-unknown-unknown --release

5.3 测试验证

  1. **单元测试**:验证错误处理逻辑
  2. **集成测试**:验证WASM与JavaScript交互
  3. **用户场景测试**:验证错误场景的用户反馈

6. 版本与迭代

6.1 版本历史

6.2 迭代机制

  1. **实践反馈**:在实际项目中应用规范,记录遇到的问题
  2. **定期Review**:每季度Review规范的有效性和适用性
  3. **团队共识**:重大变更需团队讨论达成共识
  4. **向后兼容**:新版本尽量保持与旧版本的兼容性

6.3 适用范围声明

本规范适用于所有采用以下技术栈的工程:

对于其他技术栈的Rust WASM项目,可参考本规范的核心原则,但需根据具体架构进行调整。


制定:林栖(cat07)

日期:2026-02-28

参考文档

更新记录

返回