Rust操作HashMap Entry时调用GzEncoder的finish方法报错如何解决?
报错原因
1. 多余mut警告
stdin变量不需要声明为可变,后续调用的lock()方法不需要可变所有权,直接去掉mut关键字即可消除该警告。
2. E0507移动错误
核心原因是flate2库中GzEncoder的finish()方法签名为fn finish(self) -> Result<W, Error>,该方法会直接拿走GzEncoder的所有权,调用后原编码器无法再使用。
但你通过RefCell::borrow_mut()拿到的是RefMut<GzEncoder>智能指针,只能获取编码器的可变引用,无法直接拿走它的所有权,因此编译器会报错提示无法从解引用的RefMut中移动值。
修复方案
有两种常用可行方案:
方案1:直接取出HashMap元素所有权(最小改动)
遍历HashMap时不要使用values_mut(),改用into_values()直接消费整个HashMap,拿到每个RefCell<GzEncoder>的所有权后,调用into_inner()取出内部的编码器,就可以正常调用finish()方法。同时可以补充写入换行符的逻辑,避免所有行拼接在一起,也可以把手动unwrap改为用?传播错误更符合Rust规范。
修复后代码
use std::io::BufRead; use std::io::{self}; use flate2::write::GzEncoder; use flate2::Compression; use std::fs::File; use std::io::Write; use std::cell::RefCell; use std::collections::hash_map::Entry; use std::collections::HashMap; fn main() -> io::Result<()> { // 移除多余的mut let stdin = io::stdin(); let mut gzfiles = HashMap::new(); for result in stdin.lock().lines() { let line = result?; let year: String = (&line[0..4]).into(); match gzfiles.entry(year.clone()) { Entry::Vacant(entry) => { let output = File::create(year)?; let mut encoder = GzEncoder::new(output, Compression::default()); encoder.write_all(line.as_bytes())?; // 补充换行符,避免行内容拼接 encoder.write_all(b"\n")?; entry.insert(RefCell::new(encoder)); } Entry::Occupied(mut entry) => { let mut encoder = entry.get_mut().borrow_mut(); encoder.write_all(line.as_bytes())?; encoder.write_all(b"\n")?; } } } // 直接消费HashMap拿到元素所有权 for v in gzfiles.into_values() { v.into_inner().finish().unwrap(); } Ok(()) }
方案2:移除冗余的RefCell(更简洁)
你的代码场景中完全不需要使用RefCell做内部可变性,HashMap的OccupiedEntry可以直接拿到元素的可变引用,删掉RefCell后代码更简洁,也不会产生所有权冲突问题:
修复后代码
use std::io::BufRead; use std::io::{self}; use flate2::write::GzEncoder; use flate2::Compression; use std::fs::File; use std::io::Write; use std::collections::hash_map::Entry; use std::collections::HashMap; fn main() -> io::Result<()> { let stdin = io::stdin(); let mut gzfiles = HashMap::new(); for result in stdin.lock().lines() { let line = result?; let year: String = (&line[0..4]).into(); match gzfiles.entry(year.clone()) { Entry::Vacant(entry) => { let output = File::create(year)?; let mut encoder = GzEncoder::new(output, Compression::default()); encoder.write_all(line.as_bytes())?; encoder.write_all(b"\n")?; entry.insert(encoder); } Entry::Occupied(mut entry) => { let encoder = entry.get_mut(); encoder.write_all(line.as_bytes())?; encoder.write_all(b"\n")?; } } } for v in gzfiles.into_values() { v.finish().unwrap(); } Ok(()) }
内容的提问来源于stack exchange,提问作者d33tah
相关产品推荐
相关产品推荐

