You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Wasm-bindgen调用Rust函数时触发RuntimeError: unreachable executed错误求助

Wasm-bindgen调用Rust函数时触发RuntimeError: unreachable executed错误求助

最近在做一个Web项目,用Rust编译WASM实现Abrams-Strogatz种群模型的求解,前端通过wasm-bindgen调用Rust导出的函数,但运行时一直碰到Uncaught (in promise) RuntimeError: unreachable executed错误,卡了好几天,来求大家帮忙排查!

我的代码结构

前端调用代码

import init, { solve_abrams_strogatz } from './pkg/langshift.js';

async function run() {
  await init();
  console.log("Loaded"); // 这行能正常打印
  
  Plotly.newPlot('gd', {
    "data": [{
      "y": solve_abrams_strogatz(0.5, 0.5, 1.0, 2.0, 0.5, 100.0) // 错误触发在这里
    }],
    "layout": {
      "xaxis": { "title": "Time" },
      "yaxis": { "title": "Population" },
      "title": "Abrams Strogatz Model",
      "width": 800,
      "height": 600
    }
  });
  
  console.log("Done"); // 这行永远到不了
}

run();

Rust根模块(lib.rs)

pub mod abrams_strogatz;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn solve_abrams_strogatz(x: f64, y: f64, c: f64, a: f64, s: f64, t: f64) -> Vec<f64> {
    abrams_strogatz::Community { x, y, c, a, s }
        .solve(t)
}

Abrams-Strogatz实现模块

use differential_equations::prelude::*;

#[derive(Copy, Clone)]
pub struct Community {
    pub x: f64,
    pub y: f64,
    pub c: f64,
    pub a: f64,
    pub s: f64,
}

impl ODE for Community {
    fn diff(&self, _t: f64, y: &f64, dydt: &mut f64) {
        *dydt = (1.0 - y) * self.c * y.powf(self.a) * self.s 
                - y * self.c * (1.0 - y).powf(self.a) * (1.0 - self.s);
    }
}

impl Community {
    pub fn n(&self) -> u32 {
        (self.x + self.y) as u32
    }

    pub fn solve(&self, t: f64) -> Vec<f64> {
        let problem = ODEProblem::new(
            *self,
            0.0,
            t,
            self.y / (self.n() as f64),
        );

        let mut solver = ExplicitRungeKutta::dop853()
            .rtol(1e-8)
            .atol(1e-6);

        match problem.solve(&mut solver) {
            Ok(y) => y,
            Err(e) => panic!("{}", e),
        }.iter().map(|(_, y)| *y).collect()
    }
}

编译方式

我用wasm-pack build --target web生成WASM库,依赖的differential-equations和nalgebra crate官方说明都支持WASM编译,但就是跑不起来,错误指向调用solve_abrams_strogatz的时候触发了unreachable指令。


问题排查与解决方案

核心原因:WASM环境下的Panic表现

首先,WASM里的unreachable executed几乎都是Rust代码触发了panic导致的——因为Rust默认的panic处理在WASM环境中会生成unreachable指令,而不是像原生环境那样打印堆栈。你代码里的solve方法用了panic!,大概率是微分方程求解器返回了错误,触发了panic。

步骤1:先拿到具体错误信息

我们可以把函数返回类型改成Result,把panic换成返回错误,这样就能在前端看到具体问题:

修改Rust代码

首先修改abrams_strogatz模块的solve方法,返回Result而不是panic:

pub fn solve(&self, t: f64) -> Result<Vec<f64>, String> {
    let problem = ODEProblem::new(
        *self,
        0.0,
        t,
        self.y / (self.n() as f64),
    );

    let mut solver = ExplicitRungeKutta::dop853()
        .rtol(1e-8)
        .atol(1e-6);

    match problem.solve(&mut solver) {
        Ok(y) => Ok(y.iter().map(|(_, y)| *y).collect()),
        Err(e) => Err(format!("求解器错误: {}", e)),
    }
}

然后修改lib.rs的导出函数,适配Result并转换成JS能处理的类型:

#[wasm_bindgen]
pub fn solve_abrams_strogatz(x: f64, y: f64, c: f64, a: f64, s: f64, t: f64) -> Result<Vec<f64>, JsValue> {
    abrams_strogatz::Community { x, y, c, a, s }
        .solve(t)
        .map_err(|e| JsValue::from_str(&e))
}

修改前端代码捕获错误

async function run() {
  await init();
  console.log("Loaded");
  
  try {
    const yData = await solve_abrams_strogatz(0.5, 0.5, 1.0, 2.0, 0.5, 100.0);
    Plotly.newPlot('gd', {
      "data": [{ "y": yData }],
      "layout": {
        "xaxis": { "title": "Time" },
        "yaxis": { "title": "Population" },
        "title": "Abrams Strogatz Model",
        "width": 800,
        "height": 600
      }
    });
    console.log("Done");
  } catch (err) {
    console.error("发生错误:", err);
  }
}

现在运行就能在控制台看到具体的错误原因了,比如求解器不收敛、初始值非法等。

步骤2:检查初始值计算的潜在问题

看你的初始值是self.y / (self.n() as f64),而self.n()是把x+y转成u32。如果x+y是小于1的浮点数(比如0.999999),转成u32会变成0,导致除以0触发panic。建议把n()改成返回浮点数:

pub fn n(&self) -> f64 {
    self.x + self.y
}
// 初始值改成self.y / self.n()

步骤3:排查求解器的WASM兼容性

有些高阶求解器(比如dop853)可能用到了WASM不支持的指令,或者依赖的底层库有问题。可以先换成简单的求解器测试,比如RK4:

let mut solver = ExplicitRungeKutta::rk4()
    .rtol(1e-8)
    .atol(1e-6);

步骤4:启用panic钩子,直接查看panic详情

如果还是想保留panic的方式,或者需要看堆栈信息,可以添加console_error_panic_hook crate,把Rust的panic信息输出到浏览器控制台:

Cargo.toml添加依赖

[dependencies]
console_error_panic_hook = "0.1.7"

在lib.rs添加初始化钩子

#[wasm_bindgen(start)]
pub fn init_panic_hook() {
    console_error_panic_hook::set_once();
}

这样即使触发panic,也能在浏览器控制台看到具体的panic信息和堆栈,不用再猜原因了。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.07 12:03:07