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

324 天前
 Cola98

最近接触 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)
    }
}
2122 次点击
所在节点    Rust
8 条回复
beimenjun
324 天前
你有没有试过 ChatGPT ?
Cola98
324 天前
@beimenjun 试了,但是效果一般
Donaldo
324 天前
你的问题描述的不清楚,什么叫“每次解析出来的值都会覆盖掉之前的值,导致最后结果为空”?是哪里为空?是最后返回的 rdb_info.aux_info 吗?
nagisaushio
324 天前
不知道你在问什么,是返回的 rdb_info.aux_info 为空吗
Cola98
324 天前
@Donaldo 嗯嗯是的
Cola98
324 天前
@nagisaushio 是的,就是返回 aux_info
PTLin
324 天前
来个最小 demo ,不过你要写解析库还是推荐用 nom
lijiachang
213 天前
问题所在:
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. 可以添加日志来跟踪解析过程

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://ex.noerr.eu.org/t/1073186

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX