Is there any way to return a reference to a variab

2018-12-31 03:29发布

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

2条回答
何处买醉
2楼-- · 2018-12-31 03:40

Is there any way to return a reference from a function without arguments?

No (except references to static values, but those aren't helpful here).

However, you might want to look at OpenOptions::create. If you change your first line in main to

let  f = OpenOptions::new().write(true).create(true).open(b"foo.txt");

the file will be created if it does not yet exist, which should solve your original problem.

查看更多
君临天下
3楼-- · 2018-12-31 03:58

fjh is absolutely correct, but I want to comment a bit more deeply and touch on some of the other errors with your code.

Is there any way to return a reference from a function without arguments?

Technically "yes", but for what you want, "no".

A reference points to an existing piece of memory. In a function with no arguments, the only things that could be referenced are global constants (which have the lifetime &'static) and local variables. I'll ignore globals for now.

In a language like C or C++, you could actually take a reference to a local variable and return it. However, as soon as the function returns, there's no guarantee that the memory that you are referencing continues to be what you thought it was. It might stay what you expect for a while, but eventually the memory will get reused for something else. As soon as your code looks at the memory and tries to interpret the username as the amount of money left in the bank account, problems will arise!

This is what Rust's lifetimes prevent - you aren't allowed to use a reference beyond how long the referred-to value is valid at its current memory location.

Instead of trying to return a reference, return an owned object. String instead of &str, Vec<T> instead of &[T], T instead of &T, etc.

Your actual problem

Look at the documentation for OpenOptions::open:

fn open<P: AsRef<Path>>(&self, path: P) -> Result<File>

It returns a Result<File>, so I don't know how you'd expect to return an OpenOptions or a reference to one. Your function would work if you rewrote it as:

fn trycreate() -> File {
    OpenOptions::new().write(true).open("foo.txt").expect("Couldn't open")
}

This uses Result::expect to panic with a useful error message. Of course, panicking in the guts of your program isn't super useful, so it's recommended to propagate your errors back out:

fn trycreate() -> io::Result<File> {
    OpenOptions::new().write(true).open("foo.txt")
}

And Option and Result have lots of nice methods to deal with chained error logic. Here, you can use or_else:

let f = OpenOptions::new().write(true).open("foo.txt");
let mut f = f.or_else(|_| trycreate()).expect("failed at creating");

I'd also create an "inner main" that returns a Result. All together, including fjh's suggestions:

use std::io::{self, Write};
use std::fs::OpenOptions;

fn inner_main() -> io::Result<()> {
    let mut f = OpenOptions::new()
        .create(true)
        .write(true)
        .append(true)
        .open("foo.txt")?;

    f.write(b"test1\n")?;
    f.write(b"test2\n")?;
    Ok(())
}

fn main() {
    inner_main().expect("An error occurred");
    println!("Ok");
}
查看更多
登录 后发表回答