Why do changes to a const variable not persist between usages?

徘徊边缘 提交于 2019-12-02 10:59:19

问题


I am trying to create a struct to manipulate file storage, but after I change the value it can not be used. I'm sure it's about lifetimes, but I do not understand how I can fix this.

use std::error::Error;
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader};
use std::option::Option;
use std::path::Path;

pub struct Storage<'a> {
    path_str: &'a str,
    file: Option<File>,
}

const LOCKED_STORAGE: Storage<'static> = Storage {
    path_str: &"/tmp/bmoneytmp.bms",
    file: None,
};

pub fn get_instance() -> Storage<'static> {
    if LOCKED_STORAGE.file.is_none() {
        LOCKED_STORAGE.init();
    }

    LOCKED_STORAGE
}

impl Storage<'static> {
    // Create a file for store all data, if does not alred exists
    fn init(&mut self) {
        let path = Path::new(self.path_str);

        self.file = match OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(path)
        {
            Err(e) => panic!("Couldn't create the storage file at {}", e.description()),
            Ok(file) => Some(file),
        };

        if self.file.is_none() {
            panic!("Error on init??"); // This line is not called. Ok :)
        }
    }

    // Check if section exists
    pub fn check_section(&self, name: String) -> bool {
        if self.file.is_none() {
            panic!("Error on check??"); // This line is called. :(
        }
        true
    }
}

fn main() {
    let storage = get_instance();
    storage.check_section("accounts".to_string());
}

playground

This fails:

thread 'main' panicked at 'Error on check??', src/main.rs:48:13

I am trying to use a method to open a file and read this opened file, but in second method the instance of the file is not opened. Using Option<File>, I change the value with Same/None but the variable continues to be None.


回答1:


When programming, it's a very important skill to learn how to create a Minimal, Complete, and Verifiable example. Here is one for your problem:

const EXAMPLE: Option<i32> = Some(42);

fn main() {
    EXAMPLE.take();
    println!("{:?}", EXAMPLE);
}

This prints Some(42) — the value of EXAMPLE is not changed.

A const variable has no guarantees about how many instances it will have. The compiler is allowed to have zero, one, or multiple instances of it. Every time you make use of a const, it's as if you created a brand new value right there, pasting in the definition of the constant:

fn main() {
    Some(42).take();
    println!("{:?}", Some(42));
}

Instead, you want to create a singleton.

See also:

  • What does it mean for a const type in Rust to be inlined?
  • Populating a static/const with an environment variable at runtime in Rust
  • Why are const atomic variables not updated, but static atomic variables are?



回答2:


Thanks @shepmaster. I am learned much with your answer. But i changed my approach and fix my problem using Mutex global static and lazy_static.

Also i read the article https://bryce.fisher-fleig.org/blog/strategies-for-returning-references-in-rust/index.html and it helped me to understand my error.

My new code:

#[macro_use]
extern crate lazy_static;

use std::error::Error;
use std::fs::{File, OpenOptions};
use std::io::{BufRead, BufReader};
use std::path::Path;
use std::option::Option;
use std::sync::Mutex;

pub struct Storage {
    path_str: String,
    file: Option<File>
}

lazy_static! {
    pub static ref LOCKED_STORAGE: Mutex<Storage> = Mutex::new(start_storage());
}

fn start_storage() -> Storage {
    let mut st = Storage { path_str: "/tmp/bmoneytmp.bms".to_string(), file: None };
    st.init();
    st
}

impl Storage {

    // Create a file for store all data, if does not alred exists
    fn init(&mut self) {

        let path = Path::new(&self.path_str);

        self.file = match OpenOptions::new()
            .read(true)
            .write(true)
            .create(true)
            .open(path)
        {
            Err(e) => panic!("Couldn't create the storage file at {}", e.description()),
            Ok(file) => Some(file),
        };
    }

    // Check if section exists
    pub fn check_section(&self, name: String) -> bool {

        let file = match &self.file {
            Some(file) => file,
            None => panic!("File of storage not opened")
        };

        for line in BufReader::new(file).lines() {
            println!("{}{:?}", name, line); // Working!!
        }

        true
    }
}

fn main() {
    // run in your bash before: echo "Row 1" >> /tmp/bmoneytmp.bms
    LOCKED_STORAGE.lock().unwrap().check_section("accounts".to_string());
}

You can build this on playground: https://play.rust-lang.org/?gist=bbd47a13910e0f7cda908dc82ba290eb&version=beta&mode=debug&edition=2018

Full code of my project: https://github.com/fernandobatels/blitz-money

Full code of my fix: https://github.com/fernandobatels/blitz-money/blob/9dc04742a57e6cd99742f2400a6245f210521f5d/src/backend/storage.rs https://github.com/fernandobatels/blitz-money/blob/9dc04742a57e6cd99742f2400a6245f210521f5d/src/backend/accounts.rs



来源:https://stackoverflow.com/questions/52469986/why-do-changes-to-a-const-variable-not-persist-between-usages

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!