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

代码在main中正常运行,抽取为函数时触发可变借用冲突错误

问题

使用deno_core crate执行一段小型JavaScript代码片段,获取结果对象并取出其中的test属性,将其作为函数执行。以下代码全部写在main函数中时可正常运行,但尝试将相关逻辑抽取为独立函数时编译失败。请问如何避免此类情况?为什么在main中可以运行?是否可以将这段逻辑抽取为一个函数,正确的做法是什么?

代码示例及编译错误

use deno_core::v8;
use deno_core::JsRuntime;

fn main() -> anyhow::Result<()> {
    let mut runtime = JsRuntime::new(Default::default());
    let executed = runtime.execute_script("name", "({ test: (a, b) => { console.log(a + b) } })")?;
    let scope = &mut runtime.handle_scope();
    let local_executed = v8::Local::new(scope, executed);
    let result_obj = v8::Local::<v8::Object>::try_from(local_executed)?;
    let param1 = v8::String::new(scope, "Hello").unwrap();
    let param2 = v8::String::new(scope, "World").unwrap();
    let args = vec![param1.into(), param2.into()];

    let fn_name: v8::Local<v8::String> = v8::String::new(scope, "my_function").unwrap();
    result_obj.get(scope, fn_name.into())
        .map(|value| v8::Local::<v8::Function>::try_from(value))
        .unwrap()?
        .call(scope, local_executed, &args);

    // 若调用get_fn并执行返回的v8::Function,编译失败:
    //
    // get_fn(scope, result_obj).call(scope, local_executed, &args);
    //        -----              ---- ^^^^^ 第二次可变借用发生在此处
    //        |                  |
    //        |                  第一次借用后续被call使用
    //        第一次可变借用发生在此处
    //
    // error[E0499]: cannot borrow `*scope` as mutable more than once at a time

    Ok(())
}

fn get_fn<'a>(scope: &'a mut v8::HandleScope, object: v8::Local<'a, v8::Object>) -> v8::Local<'a, v8::Function> {
    let fn_name = v8::String::new(scope, "test").unwrap();
    object.get(scope, fn_name.into())
        .map(|value| v8::Local::<v8::Function>::try_from(value))
        .unwrap()
        .unwrap()
}
解答

为什么main中可以正常运行

在main函数的链式调用result_obj.get(...).map(...).unwrap()?.call(...)里,Rust的借用检查器能识别出:get方法对scope的可变借用在map处理完成后就已结束,后续call方法的借用是全新的,不会产生重叠。链式调用的每一步都是临时值,借用的生命周期被限制在当前步骤,不会和后续步骤的借用冲突。

抽取函数时编译失败的原因

你写的get_fn函数将scope的可变借用和返回的v8::Local绑定到了同一个生命周期'a,这意味着从get_fn返回的Function会一直持有scope的可变借用,直到Function被销毁。当你调用get_fn(scope, result_obj).call(scope, ...)时,get_fn还未返回就已经把scope的可变借用占用,而call方法又需要再次借用scope,违反了Rust“同一时间只能有一个可变借用”的规则,导致编译错误。

正确抽取函数的做法

解决方法是将获取函数和执行函数拆分为两个独立步骤,让两次对scope的可变借用不会重叠。同时调整get_fn的错误处理,返回Result类型,更符合Rust最佳实践:

调整后的代码示例

use deno_core::v8;
use deno_core::JsRuntime;

fn main() -> anyhow::Result<()> {
    let mut runtime = JsRuntime::new(Default::default());
    let executed = runtime.execute_script("name", "({ test: (a, b) => { console.log(a + b) } })")?;
    let scope = &mut runtime.handle_scope();
    let local_executed = v8::Local::new(scope, executed);
    let result_obj = v8::Local::<v8::Object>::try_from(local_executed)?;
    let param1 = v8::String::new(scope, "Hello").unwrap();
    let param2 = v8::String::new(scope, "World").unwrap();
    let args = vec![param1.into(), param2.into()];

    // 拆分两步:先获取函数,再执行,避免借用重叠
    let test_fn = get_fn(scope, result_obj)?;
    test_fn.call(scope, local_executed, &args);

    Ok(())
}

fn get_fn<'a>(scope: &'a mut v8::HandleScope, object: v8::Local<'a, v8::Object>) -> anyhow::Result<v8::Local<'a, v8::Function>> {
    let fn_name = v8::String::new(scope, "test").unwrap();
    let value = object.get(scope, fn_name.into())
        .ok_or_else(|| anyhow::anyhow!("Failed to get 'test' property"))?;
    let func = v8::Local::<v8::Function>::try_from(value)
        .map_err(|_| anyhow::anyhow!("Property 'test' is not a function"))?;
    Ok(func)
}

核心原理

将操作拆分为两个独立语句后:

  1. 第一句let test_fn = get_fn(scope, result_obj)?;完成对scope的可变借用,获取到函数后,借用立即结束。
  2. 第二句test_fn.call(scope, ...)重新对scope进行可变借用,此时前一个借用已经释放,不会产生冲突。

内容的提问来源于stack exchange,提问作者almafa

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 00:20:07