V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Cola98
V2EX  ›  Rust

请教一个 Rust 问题,提前祝大家中秋节快乐

  •  
  •   Cola98 · 112 天前 · 1716 次点击
    这是一个创建于 112 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近接触 rust ,心血来潮想写一个 rdb 解析工具,目前 aux 部分解析完成,但是每次解析出来的值都会覆盖掉之前的值,导致最后结果为空,以下是代码,跪求各位大佬解答 orz:

    impl ParserFactory {
        pub fn parse(rdb_context: &[u8]) -> Result<RDBInfo> {
            let mut cursor = Cursor::new(rdb_context);
            let base_info = BaseInfo::parse(&mut cursor)?;
            let rdb_version:usize = base_info.rdb_version.parse().expect("Not a valid number");
            let mut aux_info = None;
            let mut db_info=  None;
    
    
    
            // aux 只有 rdb 版本大于等于 7 才引入
            if rdb_version < 7 {
                aux_info = None
            };
    
            loop {
                // 读取标志位
                let mut flag_byte = [0;1];
                if cursor.read_exact(&mut flag_byte).is_err(){
                    break
                }
                match flag_byte[0] {
                    FA => {
                        aux_info = Some(AuxInfo::parse(&mut cursor)?);
                    }
                    FE => {
                        db_info = Some(DbInfo::parse(&mut cursor)?);
                    }
    
                    FF => {
                        println!("parse done.");
                        break
                    }
                    _ =>{
                        continue;
                    }
                }
            }
    
            // 组合 RDBInfo
            let rdb_info = RDBInfo {
                base_info,
                aux_info: aux_info.unwrap_or_else(|| AuxInfo {
                    redis_server_version: String::new(),
                    used_mem:0,
                }),
            };
    
            Ok(rdb_info)
        }
    
    }
    
    impl Parser for AuxInfo {
        fn parse(cursor: &mut Cursor<&[u8]>) -> Result<AuxInfo> {
            let mut aux_info = AuxInfo {
                redis_server_version: String::new(),
                used_mem: 0,
            };
            let aux_name = parser_aux_name(cursor)?;
    
            match aux_name.as_str() {
                "redis-ver" => {
                    aux_info.redis_server_version = parser_aux_value(cursor)?;
                }
                "used-mem" => {
                    aux_info.used_mem = parser_aux_value(cursor)?.parse::<usize>().expect("Failed to parse used-mem");
                }
    
                _ => {
    
                }
            }
            Ok(aux_info)
        }
    }
    
    8 条回复    2025-01-04 23:42:55 +08:00
    beimenjun
        1
    beimenjun  
       112 天前
    你有没有试过 ChatGPT ?
    Cola98
        2
    Cola98  
    OP
       112 天前
    @beimenjun 试了,但是效果一般
    Donaldo
        3
    Donaldo  
       112 天前
    你的问题描述的不清楚,什么叫“每次解析出来的值都会覆盖掉之前的值,导致最后结果为空”?是哪里为空?是最后返回的 rdb_info.aux_info 吗?
    nagisaushio
        4
    nagisaushio  
       112 天前 via Android
    不知道你在问什么,是返回的 rdb_info.aux_info 为空吗
    Cola98
        5
    Cola98  
    OP
       111 天前
    @Donaldo 嗯嗯是的
    Cola98
        6
    Cola98  
    OP
       111 天前
    @nagisaushio 是的,就是返回 aux_info
    PTLin
        7
    PTLin  
       111 天前
    来个最小 demo ,不过你要写解析库还是推荐用 nom
    lijiachang
        8
    lijiachang  
       1 天前
    问题所在:
    1. 在主循环中,每次遇到 FA 标志时都创建了新的 AuxInfo ,且覆盖了之前的值
    2. AuxInfo::parse 方法每次只处理一个字段,而不是累积所有字段

    解决方案,我们有两种处理方式:

    方案 1 - 修改 ParserFactory::parse 方法,使用 Vec 收集所有 AUX 信息,最后合并:

    ```rust
    impl ParserFactory {
    pub fn parse(rdb_context: &[u8]) -> Result<RDBInfo> {
    let mut cursor = Cursor::new(rdb_context);
    let base_info = BaseInfo::parse(&mut cursor)?;
    let rdb_version: usize = base_info.rdb_version.parse().expect("Not a valid number");
    let mut aux_infos = Vec::new(); // 存储所有 AUX 信息
    let mut db_info = None;

    if rdb_version < 7 {
    aux_infos = Vec::new();
    };

    loop {
    let mut flag_byte = [0;1];
    if cursor.read_exact(&mut flag_byte).is_err() {
    break
    }
    match flag_byte[0] {
    FA => {
    if let Some(aux_info) = AuxInfo::parse(&mut cursor)? {
    aux_infos.push(aux_info);
    }
    }
    FE => {
    db_info = Some(DbInfo::parse(&mut cursor)?);
    }
    FF => {
    println!("parse done.");
    break
    }
    _ => {
    continue;
    }
    }
    }

    // 合并所有 AUX 信息
    let final_aux_info = aux_infos.into_iter().fold(
    AuxInfo {
    redis_server_version: String::new(),
    used_mem: 0,
    },
    |mut acc, curr| {
    if !curr.redis_server_version.is_empty() {
    acc.redis_server_version = curr.redis_server_version;
    }
    if curr.used_mem != 0 {
    acc.used_mem = curr.used_mem;
    }
    acc
    }
    );

    let rdb_info = RDBInfo {
    base_info,
    aux_info: final_aux_info,
    };

    Ok(rdb_info)
    }
    }
    ```

    方案 2 - 修改 AuxInfo::parse 方法,让它保持当前状态:

    ```rust
    impl Parser for AuxInfo {
    fn parse(cursor: &mut Cursor<&[u8]>) -> Result<Option<AuxInfo>> {
    let aux_name = parser_aux_name(cursor)?;

    // 根据字段名返回对应的 AuxInfo
    let aux_info = match aux_name.as_str() {
    "redis-ver" => {
    Some(AuxInfo {
    redis_server_version: parser_aux_value(cursor)?,
    used_mem: 0,
    })
    }
    "used-mem" => {
    Some(AuxInfo {
    redis_server_version: String::new(),
    used_mem: parser_aux_value(cursor)?.parse::<usize>()
    .expect("Failed to parse used-mem"),
    })
    }
    _ => None
    };

    Ok(aux_info)
    }
    }
    ```

    我建议使用方案 1 ,因为:
    1. 更清晰地表达了数据收集的过程
    2. 更容易扩展,如果将来需要添加新的 AUX 字段
    3. 数据处理逻辑更集中,便于维护

    关键改进点:
    1. 使用 Vec 收集所有 AUX 信息
    2. 使用 fold 方法合并所有 AUX 信息
    3. 保留了非空/非零值,避免数据丢失

    你需要注意的地方:
    1. 确保 parser_aux_name 和 parser_aux_value 正确处理了数据
    2. 考虑错误处理机制,可能需要添加更多的错误类型
    3. 可以添加日志来跟踪解析过程
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   929 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 21ms · UTC 21:34 · PVG 05:34 · LAX 13:34 · JFK 16:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.