I'm trying to build an abstraction over a file, where the content is stored in memory and a hash is computed before writing it out to the filesystem, which should happen in the close()
method.
use std::path::PathBuf;
use sha2::{Digest, Sha256};
fn main() {
let mut fwh = FileWithHash::new(PathBuf::from("somepath.txt"));
fwh.write("first line\n");
fwh.write("second line\n");
fwh.close();
}
struct FileWithHash {
path: PathBuf,
hash: Option<String>,
content: Vec<String>,
pub hasher: Sha256,
}
impl FileWithHash {
pub fn new(path: PathBuf) -> FileWithHash {
FileWithHash {
path,
hash: None,
content: Vec::new(),
hasher: Sha256::new(),
}
}
pub fn write(&mut self, content: &str) {
self.hasher.update(content.as_bytes());
self.content.push(content.to_string());
}
pub fn close(&mut self) {
// compute the final hash
// signature: fn finalize(self) -> Output<Self>;
// it consumes self
// error: cannot move out of `self.hasher` which is behind a mutable reference
self.hash = Some(format!("{:x}", self.hasher.finalize()));
// write the file the path
// ...
}
}
the problem I have is that the self.hasher.finalize()
method consumes the hasher
which is part of self
, which is itself &mut
. I think I understand why this doesn't work, but I cannot come up with a reasonable fix.
I've tried extracting the logic into a function like
pub fn compute_hash(hasher: Sha256) -> String {
format!("{:x}", hasher.finalize())
}
but then I run into issues with partially moved values (makes sense).
Any ideas about what the proper pattern should be here?
Thanks for your help.
@justinas, I've tried the Option
suggestion.
the methods become
pub fn write(&mut self, content: String) {
match &mut self.hasher {
Some(h) => h.update(content.as_bytes()),
None => {}
}
self.size = self.size + content.len();
self.content.push(content);
}
pub fn close(&mut self) {
self.hash = Some(format!("{:x}", self.hasher.take().unwrap().finalize()));
// write the file the path
// ...
}
Does this look OK to you?