Search code examples
windowsfilerustio

rust: weird behaviour from a "file repository" struct


I was running some tests to see what is the best way to implement some kind of repository struct containing every file used by the program for an application I want to develop. (if this makes any sense).

This is what I made:

use std::path::Path;
use std::fs::{File, OpenOptions};
use std::io::{Write, Read};

#[derive(Debug)]
struct Repo<'a> {
    path: Box<&'a Path>,
}

impl Repo<'_> {
    pub fn new() -> Self {
        Self { path: Box::new(Path::new("test.txt")) }
    }

    pub fn get_mut(&self) -> File {
        OpenOptions::new().write(true).read(false).open(&*self.path).unwrap()
    }

    pub fn get_imm(&self) -> File {
        OpenOptions::new().write(false).read(true).open(&*self.path).unwrap()
    }
}

fn main() {
    let f = Repo::new();

    let mut f_mut = f.get_mut();
    let mut f_imm = f.get_imm();

    let mut buf1 = String::new();
    f_imm.read_to_string(&mut buf1).unwrap();    
    println!("1: {}", buf1);

    f_mut.write("123456".to_string().as_bytes()).unwrap();

    let mut buf2 = String::new();
    f_imm.read_to_string(&mut buf2).unwrap();
    println!("2: {}", buf2);
}

Imagine having an empty "test.txt" file, the expected output from the two println!() would be:

1:
2: 123456

and in fact it is. bu if i try to change the written value ("123456") to something else and then recompile and run the program some weird things happen; sometimes the expected value of the second output is missing some characters or is completely empty. this code behaves correctly only when "test.txt" is empty or just by seemingly random chance.

could someone explain to me why this happens and why this code is probably not very good practice? or maybe it's a problem with my machine or even the rustc itself.

running this on windows 10. (also english is not my first language)


Solution

  • You seem to be opening the file twice with different access modes at the same time. What probably is happening is the operating system has not finished writing the data passed to f_mut to the file.

    This is because file I/O operations incorporate buffering to increase performance, and the buffer gets flushed occasionally, but the frequency of it is not universally defined. Luckily, it is possible to flush the data manually, before reading the contents.

    The code would not be the best practice, due to the fact that you are keeping 2 references to the same file open, and the said buffering does not work nicely when the handles are separate. Keeping the structure the same, I would open a file with both read and write access, and abstract away the reading/writing operations. For individual reads/writes, you would need to perform file seeking - seek to the end of the file before writing, and seek to the start of the file before reading.