I'm relatively new to Rust, and running into an idiosyncracy with how I'd write a constructor in another language. I have a struct, S3Logger, which creates a temporary on disk file, and when a certain amount of data is written to this file will upload to S3 and rotate to another file.
I would like my new function to use a method on the class, gen_new_file, which will open a new file with the right name in the write location. However, in order to make it a method, I must already have a S3Logger to pass in, which I don't have yet during the new function. In the example below, I show how I might do this in another language, to partially construct the object before using object methods to finish the construction; however this obviously doesn't work in Rust.
I could use an Option for the current_log_file, but this feels a little gross. I'd like to enforce the invariant that if I have an S3Logger, I know it has an open file.
What is the best practice in Rust for such problems?
pub struct S3Logger {
local_folder: PathBuf,
destination_path: String,
max_log_size: usize,
current_log_file: File,
current_logged_data: usize
}
impl S3Logger {
pub fn new<P: AsRef<Path>>(
local_folder: P,
destination_path: &str,
max_log_size: usize
) -> Self {
std::fs::create_dir_all(&local_folder).unwrap();
let mut ret = Self {
local_folder: local_folder.as_ref().to_path_buf(),
destination_path: destination_path.to_string(),
max_log_size: max_log_size,
current_logged_data: 0
};
// fails ^^^^ missing `current_log_file
ret.gen_new_file();
return ret
}
fn gen_new_file(&mut self) -> () {
let time = Utc::now().to_rfc3339();
let file_name = format!("{}.log", time);
let file_path = self.local_folder.join(file_name);
self.current_log_file = File::create(&file_path).unwrap();
}
}