Is there any way to return a reference to a variable created in

ghz 7months ago ⋅ 93 views

Is there any way to return a reference to a variable created in a function?

I want to write a program that will write a file in 2 steps. It is likely that the file may not exist before the program is run. The filename is fixed.

The problem is that OpenOptions.new().write() can fail. In that case, I want to call a custom function trycreate(). The idea is to create the file instead of opening it and return a handle. Since the filename is fixed, trycreate() has no arguments and I cannot set a lifetime of the returned value.

How can I resolve this problem?

use std::io::Write;
use std::fs::OpenOptions;
use std::path::Path;

fn trycreate() -> &OpenOptions {
    let f = OpenOptions::new().write(true).open("foo.txt");
    let mut f = match f {
        Ok(file)  => file,
        Err(_)  => panic!("ERR"),
    };
    f
}

fn main() {
    {
        let f = OpenOptions::new().write(true).open(b"foo.txt");
        let mut f = match f {
            Ok(file)  => file,
            Err(_)  => trycreate("foo.txt"),
        };
        let buf = b"test1\n";
        let _ret = f.write(buf).unwrap();
    }
    println!("50%");
    {
        let f = OpenOptions::new().append(true).open("foo.txt");
        let mut f = match f {
            Ok(file)  => file,
            Err(_)  => panic!("append"),
        };
        let buf = b"test2\n";
        let _ret = f.write(buf).unwrap();
    }
    println!("Ok");
}

Answers

To handle the situation where you want to try opening a file and, if that fails, create it, you need to make some changes to your approach. Specifically, you should return a File handle from the trycreate function instead of an OpenOptions reference. This is because the File struct represents an actual open file, whereas OpenOptions is just a builder for opening files.

Here is an updated version of your code that should work:

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

fn trycreate() -> File {
    let f = OpenOptions::new().write(true).create(true).open("foo.txt");
    match f {
        Ok(file) => file,
        Err(_) => panic!("Failed to create or open file"),
    }
}

fn main() {
    {
        let f = OpenOptions::new().write(true).open("foo.txt");
        let mut f = match f {
            Ok(file) => file,
            Err(_) => trycreate(),
        };
        let buf = b"test1\n";
        let _ret = f.write(buf).unwrap();
    }
    println!("50%");
    {
        let f = OpenOptions::new().append(true).open("foo.txt");
        let mut f = match f {
            Ok(file) => file,
            Err(_) => panic!("Failed to open file for appending"),
        };
        let buf = b"test2\n";
        let _ret = f.write(buf).unwrap();
    }
    println!("Ok");
}

Explanation:

  1. Function trycreate:

    • It now returns a File handle directly.
    • The OpenOptions::new().write(true).create(true).open("foo.txt") ensures that the file is created if it does not exist.
  2. Main Function:

    • The first block attempts to open the file for writing. If it fails (i.e., the file doesn't exist), it calls trycreate to create the file.
    • The second block opens the file for appending. If this fails, it panics because by this time, the file should exist.

Why this works:

  • The File type in Rust is designed to handle file operations. By returning a File handle from trycreate, you ensure that the file is open and ready for writing.
  • The create(true) flag in OpenOptions ensures that if the file doesn't exist, it will be created.
  • By using pattern matching on the result of open, you can gracefully handle the case where the file does not exist and fall back to creating it.

This approach should achieve the desired functionality of ensuring the file is created if it does not already exist and allows writing to it in two steps.