【问题标题】:How to call struct methods inside constructor?如何在构造函数中调用结构方法?
【发布时间】:2021-03-28 07:11:55
【问题描述】:

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

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

我可以将Option 用于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();
    }
}

【问题讨论】:

    标签: rust


    【解决方案1】:

    最简单的方法是让gen_new_file 使用local_folder: P 而不是&amp;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()
        }
    }
    

    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;
    }
    

    【讨论】:

    • 谢谢,这就是我最终要做的。我本来希望能够隐藏这一点,并让 gen_new_file 突变一个 self 参数,但想看看人们在这些情况下是否使用了一般模式。
    【解决方案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
        }
    }
    

    【讨论】:

    • 谢谢,这看起来像是我将来可以使用的模式。
    猜你喜欢
    • 2014-02-17
    • 1970-01-01
    • 2011-06-19
    • 2015-10-01
    • 2018-08-19
    • 2011-06-28
    • 1970-01-01
    相关资源
    最近更新 更多