如何在构造函数中调用结构体方法?

spr*_*121 7 rust

我对 Rust 比较陌生,并且遇到了如何用另一种语言编写构造函数的特殊情况。我有一个结构体S3Logger,它在磁盘上创建一个临时文件,当将一定量的数据写入该文件时,该文件将上传到 S3 并旋转到另一个文件。

我希望我的new函数使用类 上的方法gen_new_file,它将在写入位置打开一个具有正确名称的新文件。但是,为了使其成为一种方法,我必须已经有一个S3Logger要传入的参数,而在函数期间我还没有传入该new参数。在下面的示例中,我展示了如何用另一种语言执行此操作,在使用对象方法完成构造之前部分构造对象;但这显然在 Rust 中不起作用。

我可以使用 anOption来表示current_log_file,但这感觉有点恶心。我想强制执行以下不变量:如果我有一个S3Logger,我就知道它有一个打开的文件。

对于此类问题,Rust 的最佳实践是什么?

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

seb*_*etz 7

最简单的方法是使用gen_new_filetakelocal_folder: P而不是&mut self,从那里返回File并在构造函数中调用它:

use std::fs::File;
use std::path::{Path, PathBuf};
use chrono::Utc;

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 current_log_file = Self::gen_new_file(&local_folder);
        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,
            current_log_file,
        }
    }

    fn gen_new_file<P: AsRef<Path>>(local_folder: P) -> File {
        let time = Utc::now().to_rfc3339();
        let file_name = format!("{}.log", time);
        let file_path = local_folder.as_ref().join(file_name);
        File::create(&file_path).unwrap()
    }
}
Run Code Online (Sandbox Code Playgroud)

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=ee447eabc799035e8db246d3bccb225b

如果以后需要更换current_log_file,也可以按照同样的方法进行:

fn replace_log_file<P: AsRef<Path>>(&mut self, new_path: P) {
    let new_file = Self::gen_new_file(&new_path);
    self.current_log_file = new_file;
}
Run Code Online (Sandbox Code Playgroud)


小智 2

正如您所指出的,一种方法确实是使该字段成为可选字段。

您还可以将结构分为两部分。

struct S3LoggerInternal {
    pub local_folder: PathBuf,
    pub destination_path: String,
    pub max_log_size: usize,
}
pub struct S3Logger {
    internal: S3LoggerInternal,
    current_log_file: File,
    current_logged_data: usize,
}

impl S3Logger {
    pub fn new(...) -> Result<Self, ...> {
        let internal = S3LoggerInternal {...};
        let file = internal.open_file()?;
        Self {internal, current_log_file: file, current_logged_data: 0}
    }
}

impl S3LoggerInternal {
    fn open_file(&self) -> Result<File, ...> {
        // ... The code in your gen_new_file function
    }
}
Run Code Online (Sandbox Code Playgroud)