【rust/egui】(三)看看template的app.rs:序列化、持久化存储

说在前面

  • rust新手,egui没啥找到啥教程,这里自己记录下学习过程
  • 环境:windows11 22H2
  • rust版本:rustc 1.71.1
  • egui版本:0.22.0
  • eframe版本:0.22.0
  • 上一篇:这里

serde

  • app.rs中首先定义了我们的TemplateApp结构体
    /// 继承序列化以及反序列化 用于存储一些状态数据
    #[derive(serde::Deserialize, serde::Serialize)]
    #[serde(default)] // 在反序列化时,缺少的字段会使用Default特征对应的值进行初始化
    pub struct TemplateApp {
        // Example stuff:
        label: String,
    
        // 声明该字段跳过序列化
        #[serde(skip)]
        value: f32,
    }
    // 为TemplateApp实现Default特征
    impl Default for TemplateApp {
        fn default() -> Self {
            Self {
                // Example stuff:
                label: "Hello World!".to_owned(),
                value: 2.7,
            }
        }
    }
    
  • 在定义TemplateApp时,我们让其继承了serde::Deserialize, serde::Serializeserde是rust中用于序列化和反序列化(serialize and deserialize)一个框架。详细见这里
  • eframe中,我们使用的是ron提供的序列化实现,与json类似,但并不一致,例如以下是一个ron序列化的结果:
    Scene( // class name is optional
        materials: { // this is a map
            "metal": (
                reflectivity: 1.0,
            ),
            "plastic": (
                reflectivity: 0.5,
            ),
        },
        entities: [ // this is an array
            (
                name: "hero",
                material: "metal",
            ),
            (
                name: "monster",
                material: "plastic",
            ),
        ],
    )
    
    详细请参考https://github.com/ron-rs/ron
  • 看一个简单的ron序列化例子
    use serde::{Deserialize, Serialize};
    
    #[derive(Debug, Deserialize, Serialize)]
    struct MyStruct {
        boolean: bool,
        float: f32,
    }
    
    impl MyStruct {
        fn new() -> Self {
            return ron::from_str("(boolean: true, float: 1.23)").unwrap();
        }
    }
    
    fn main() {
        let x = MyStruct::new();
        println!("RON: {}", ron::to_string(&x).unwrap());
    }
    // output:
    // RON: (boolean:true,float:1.23)
    
  • 关于更深层次的内容这里就不再展开了 (咱也展开不下去)

持久化存储

  • 有了serde之后我们可以干什么呢?让我们继续看代码:
    impl TemplateApp {
        /// 在第一帧之前调用
        pub fn new(cc: &eframe::CreationContext<'_>) -> Self {
            // 我们也可以在这里定义我们的界面样式 使用`cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`.
            
            // 加载一些应用状态(比如上一次打开了那些文件之类的) 但是我们必须启用`persistence`特性
            if let Some(storage) = cc.storage {
            	// 这里我们使用ron取出存入的状态数据 并将其反序列化成TemplateApp
                return eframe::get_value(storage, eframe::APP_KEY).unwrap_or_default();
            }
        
            Default::default()
        }
    }
    
    impl eframe::App for TemplateApp {
        /// 在应用关闭前调用 用于存储状态
        fn save(&mut self, storage: &mut dyn eframe::Storage) {
        	// 这里我们使用ron将TemplateApp序列化 并且存入对应的文件中
            eframe::set_value(storage, eframe::APP_KEY, self);
        }
    
  • 首先我们为TemplateApp实现了new方法
  • 而在之前的main.rs中,我们可以看到该方法的调用,实际上,该函数是在eframe的各种准备工作完成后,才进行的回调
    eframe::run_native(
            "demo app",
            native_options,
            Box::new(|cc| Box::new(demo_app::TemplateApp::new(cc))),
        )
    
  • 在该函数中,我们完成了一些状态数据的还原,即读取应用上一次的工作状态,那么这些数据又是在什么时候存储下来的呢?以及存在了哪里呢?
  • 在接下来的代码中,我们实现了eframe::App特征,在save方法中,我们对状态数据进行了存储,我们可以看看eframe::set_value的具体实现:
    #[cfg(feature = "ron")]
    pub fn set_value<T: serde::Serialize>(storage: &mut dyn Storage, key: &str, value: &T) {
    	// 首先对TemplateApp进行序列化
        match ron::ser::to_string(value) {
        	// 如果序列化成功 那么进一步进行存储 这里并不会立即写文件
            Ok(string) => storage.set_string(key, string),
            // 失败则打印日志
            Err(err) => log::error!("eframe failed to encode data using ron: {}", err),
        }
    }
    
  • 我们可以运行一下应用看看效果,先修改输入:
    【rust/egui】(三)看看template的app.rs:序列化、持久化存储_第1张图片
    关闭应用后再打开:
    【rust/egui】(三)看看template的app.rs:序列化、持久化存储_第2张图片
    可以看到字符串确实保持一致,而数值已经变回原样了。
  • 那我们的数据到底存储在哪里呢?参照上一节的做法,将eframe的日志输出打开,可以看到存储路径打印出来了
    [2023-08-19T09:26:27Z DEBUG eframe] Using the glow renderer
    [2023-08-19T09:26:27Z DEBUG eframe::native::run] Entering the winit event loop (run_return)[2023-08-19T09:26:27Z DEBUG eframe::native::file_storage] Loading app state from "C:\\Users\\xxxx\\AppData\\Roaming\\demo app\\data\\app.ron"
  • 打开文件,可以看到存储的内容确实在,其中还存储了一些其他数据
    【rust/egui】(三)看看template的app.rs:序列化、持久化存储_第3张图片
  • 当我们直接修改对应的数据后再打开应用,对应的数据也发生了变化:
    【rust/egui】(三)看看template的app.rs:序列化、持久化存储_第4张图片
  • 既然是单个文件存储,那么是否会有竞争问题呢?我们打开两个应用A,B
    【rust/egui】(三)看看template的app.rs:序列化、持久化存储_第5张图片
    A想要改字符串,B同时改了字符串和数值,B先关闭,A后关闭
    【rust/egui】(三)看看template的app.rs:序列化、持久化存储_第6张图片
    再次打开应用,B修改的数据丢失了
    【rust/egui】(三)看看template的app.rs:序列化、持久化存储_第7张图片
    因此在开发/使用的时候需要注意多窗口下的数据存储问题

参考

  • serde
  • serde api
  • ron

你可能感兴趣的:(Rust,rust,开发语言,后端)