Подтвердить что ты не робот

Есть ли способ вернуть ссылку на переменную, созданную в функции?

Я хочу написать программу, которая будет писать файл за 2 шага. Вероятно, файл может не существовать до запуска программы. Имя файла исправлено.

Проблема в том, что OpenOptions.new().write() может выйти из строя. В этом случае я хочу вызвать пользовательскую функцию trycreate(). Идея состоит в том, чтобы создать файл, а не открывать его и возвращать дескриптор. Поскольку имя файла исправлено, trycreate() не имеет аргументов, и я не могу установить время жизни возвращаемого значения.

Как я могу решить эту проблему?

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

Ответ 1

fjh абсолютно корректен, но я хочу более подробно прокомментировать и затронуть некоторые другие ошибки с вашим кодом.

Есть ли способ вернуть ссылку из функции без аргументов?

Технически "да", но для чего вы хотите "нет".

Ссылка указывает на существующую часть памяти. В функции без аргументов единственными вещами, на которые можно ссылаться, являются глобальные константы (которые имеют время жизни &'static) и локальные переменные. Сейчас я буду игнорировать глобальные переменные.

На языке, подобном C или С++, вы можете фактически ссылаться на локальную переменную и возвращать ее. Однако, как только функция вернется, не гарантирует, что память, на которую вы ссылаетесь, по-прежнему остается тем, что вы считали. Он может остаться тем, что вы ожидаете какое-то время, но в конечном итоге память будет использоваться повторно для чего-то другого. Как только ваш код просматривает память и пытается интерпретировать имя пользователя как сумму денег, оставшуюся на банковском счете, возникают проблемы!

Это то, что предотвращает время жизни Rust - вам не разрешено использовать ссылку за пределами того, как долго указанное значение действительно в текущей ячейке памяти.

Вместо того, чтобы пытаться вернуть ссылку, верните принадлежащий ему объект. String вместо &str, Vec<T> вместо &[T], T вместо &T и т.д.

Ваша фактическая проблема

Посмотрите документацию для OpenOptions::open:

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

Он возвращает Result<File>, поэтому я не знаю, как вы ожидаете вернуть OpenOptions или ссылку на него. Ваша функция будет работать, если вы переписали ее как:

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

Используется Result::expect для паники с полезным сообщением об ошибке. Конечно, паника в кишках вашей программы не очень полезна, поэтому рекомендуется распространять ваши ошибки:

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

И Option и Result есть много хороших методов для обработки цепочечной логики ошибок. Здесь вы можете использовать or_else:

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

Я также создаю "внутреннюю основную", которая возвращает Result. Все вместе, включая предложения fjh:

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

Ответ 2

Есть ли способ вернуть ссылку из функции без аргументов?

Нет (кроме ссылок на статические значения, но здесь это не полезно).

Однако вы можете посмотреть OpenOptions::create. Если вы измените первую строку в main на

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

файл будет создан, если он еще не существует, что должно решить вашу исходную проблему.