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

Rust解析CLI参数写入JSON时fn项无法捕获动态环境报错排查

报错根因

你看到的can't capture dynamic environment in a fn item use the || { ... } closure form instead编译错误,核心原因是:
Rust中使用fn关键字定义的具名函数是静态函数,哪怕嵌套写在其他函数内部,也不具备捕获外层作用域变量的能力,只有闭包|| { ... }才能捕获外层环境的动态变量。

你的代码里触发报错的具体位置是main函数内部嵌套定义的handle_add_new_app具名函数:这个函数内部直接引用了外层main作用域下创建的app变量,但fn项没有环境捕获能力,因此编译器直接报错。你在该函数内部写了闭包不代表外层的fn本身会变成闭包,这是核心理解偏差点。

代码中存在的其他问题

除了直接触发报错的闭包规则误用,你的代码还有多处逻辑和类型错误,就算绕过当前报错也无法正常运行:

  • AppParams::new方法错误返回了Box<dyn Fn(AppParams) -> AppParams>闭包类型,而非AppParams结构体实例,后续所有依赖该类型的逻辑全不匹配。
  • ChainParams::new方法中调用AppParams::new拿到闭包后,直接把闭包当构造函数调用传入AppParams实例,类型完全不匹配。
  • 你自定义的convert_to_u8方法直接将闭包的裸内存转成字节切片写入文件是完全错误的持久化方式:闭包是包含函数指针、捕获变量的胖指针,直接导出内存得到的是当前进程地址空间的无效指针值,根本不是可解析的JSON格式数据,写入文件的内容全是垃圾数据。
  • CLI参数读取索引错误:env::args()返回的迭代器第一个元素是当前程序自身的执行路径,不是传入的业务参数,你从索引0开始取业务参数会导致所有参数取值错误。
  • 在main中定义了async fn但既没有调用,也没有引入任何异步运行时(如tokio)执行,逻辑永远不会生效。
  • 没有做传入参数数量校验,当用户传参不足时会直接触发数组越界panic。
修复步骤
  1. 删除main函数内部嵌套定义的convert_to_u8、handle_add_new_app两个具名函数,不要在函数内部定义fn来捕获外层变量,需要捕获环境直接使用闭包,或通过参数显式传递变量。
  2. 修正所有结构体的new方法,去掉无意义的闭包包装,直接返回结构体实例。
  3. 放弃裸指针转字节的错误序列化方式,给所有需要持久化的结构体添加serde::Serialize派生宏,直接用serde_json将结构体序列化为合法JSON字符串后写入文件。
  4. 修正CLI参数索引,从索引1开始读取业务参数,添加参数数量校验逻辑。
修正后核心代码示例

首先修改utils.rs中结构体和方法的定义:

use std::{error::Error, collections::HashMap};
use serde_json::Value;

// 给需要序列化/反序列化的结构体添加对应宏
#[derive(serde::Serialize, serde::Deserialize)]
pub struct Addresses{
    pub to_address: String,
    pub from_address: String,
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct TokenParams {
    pub token_name: String,
    pub token_address: String,
}

impl TokenParams{
    pub  fn new(token_name: &str, token_address: &str) -> Self {
        TokenParams {
            token_address: token_address.to_string(),
            token_name: token_name.to_string(),
        }
    }
    pub fn get_token(chain: &str, app: &str) -> Result<Vec<Value>, Box<dyn Error>>{
        let config = std::fs::read_to_string("Config.json").unwrap();
        let token_getter = serde_json::from_str::<Value>(&config).unwrap();
        let chain_elements = token_getter[chain][app].as_array().unwrap();
        Ok(chain_elements.to_vec())
    }
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct AppParams {
    pub appname: String,
    pub abi_endpoint: String,
    pub app_address: String,
    #[serde(flatten)]
    pub tokens: HashMap<String, String>
}

impl AppParams{
    // 修正new方法,直接返回AppParams实例,不再返回闭包
    pub fn new(appname: &str, abi_endpoint: &str, address: &str, tokens: &HashMap<String, String>) -> Self {
        AppParams {
            appname: appname.to_string(),
            abi_endpoint: abi_endpoint.to_string(),
            tokens: tokens.clone(),
            app_address: address.to_string(),
        }
    }
    pub fn get_app_params(self, chain: &str) -> Result<Vec<Value>, Box<dyn Error>>{
        let config = std::fs::read_to_string("Config.json").unwrap();
        let app_getter = serde_json::from_str::<Value>(&config).unwrap();
        let chain_elements = app_getter[chain][self.appname].as_array().unwrap();
        Ok(chain_elements.to_vec())
    }
}

#[derive(serde::Serialize, serde::Deserialize)]
pub struct ChainParams {
    pub chain_id: String,
    pub chain_address: String,
    #[serde(flatten)]
    pub chain_application: Vec<AppParams>,
}

impl ChainParams{
    // 修正new方法,直接接收AppParams实例构造
    pub fn new(chain_id: &str, chain_address: &str, chain_application: AppParams ) -> Self {
        ChainParams {
            chain_id: chain_id.to_string(),
            chain_address: chain_address.to_string(),
            chain_application: vec![chain_application],
        }
    }
    pub fn get_chain_params(self) -> Result<Vec<Value>, Box<dyn Error>>{
        let config = std::fs::read_to_string("Config.json").unwrap();
        let app_getter = serde_json::from_str::<Value>(&config).unwrap();
        let chain_elements = app_getter[self.chain_id].as_array().unwrap();
        Ok(chain_elements.to_vec())
    }
}

再修改main.rs的逻辑:

use std::collections::HashMap;
use std::fs::OpenOptions;
use std::io::Write;
use std::env;
mod utils;
use utils::*;

fn main() {
    let args: Vec<String> = env::args().collect();
    // 校验参数数量,程序路径+7个业务参数共8个
    if args.len() < 8 {
        eprintln!("参数错误,用法: <chain_id> <chain_address> <appname> <abi_endpoint> <app_address> <token_name> <token_address>");
        std::process::exit(1);
    }
    // 业务参数从索引1开始读取
    let chain_id = &args[1];
    let chain_address = &args[2];
    let appname = &args[3];   
    let abi_endpoint = &args[4];
    let app_address = &args[5];
    let token_name = &args[6];
    let token_address = &args[7];

    let new_token = TokenParams::new(token_name, token_address);
    let mut chain_tokens = HashMap::new();
    chain_tokens.insert(
        new_token.token_name,
        new_token.token_address,
    );
    
    let app = AppParams::new(
        appname, abi_endpoint, app_address, &chain_tokens
    );

    let chain_config = ChainParams::new(chain_id, chain_address, app);
    // 序列化为格式化JSON
    let json_content = serde_json::to_string_pretty(&chain_config).expect("配置序列化失败");
    let mut updated_file = OpenOptions::new().append(true).open("config.json").expect("无法打开配置文件"); 
    updated_file.write_all(json_content.as_bytes()).expect("写入配置失败");
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.31 12:18:18