web-dev-qa-db-ja.com

関数で作成された変数への参照を返す方法はありますか?

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

fjhは絶対に正しい ですが、もう少し詳しくコメントして、コードのその他のエラーに触れたいと思います。


参照を返す小規模な例から始めて、エラーを見てみましょう。

fn try_create<'a>() -> &'a String {
    &String::new()
}

Rust 2015

error[E0597]: borrowed value does not live long enough
 --> src/lib.rs:2:6
  |
2 |     &String::new()
  |      ^^^^^^^^^^^^^ temporary value does not live long enough
3 | }
  | - temporary value only lives until here
  |
note: borrowed value must be valid for the lifetime 'a as defined on the function body at 1:15...
 --> src/lib.rs:1:15
  |
1 | fn try_create<'a>() -> &'a String {
  |               ^^

Rust 2018

error[E0515]: cannot return reference to temporary value
 --> src/lib.rs:2:5
  |
2 |     &String::new()
  |     ^-------------
  |     ||
  |     |temporary value created here
  |     returns a reference to data owned by the current function

引数なしで関数から参照を返す方法はありますか?

技術的には「はい」ですが、あなたが望むものは「いいえ」です。

参照は、既存のメモリを指します。引数のない関数では、参照できるのはグローバル定数(ライフタイム&'staticを持つ)とローカル変数のみです。今はグローバルを無視します。

CやC++などの言語では、実際にローカル変数への参照を取得して返すことができます。ただし、関数が戻るとすぐに、何の保証もありません、参照しているメモリが、あなたが思っていたものであり続けることを保証します。しばらくは期待したもののままかもしれませんが、最終的にはメモリは他の何かに再利用されます。コードがメモリを調べて、ユーザー名をユーザーの銀行口座に残っている金額と解釈しようとすると、問題が発生します!

これは、Rustのライフタイムが防ぐものです-現在のメモリ位置で参照される値が有効である期間を超えて参照を使用することは許可されません。

参照を返そうとする代わりに、所有オブジェクトを返します。 &strの代わりにStringVec<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")
}

OptionResultには、連鎖エラーロジックに対処するための多くのNiceメソッドがあります。ここでは、 or_else を使用できます。

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

Resultからmainも返します。すべて一緒に、fjhの提案を含めて:

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

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

    f.write_all(b"test1\n")?;
    f.write_all(b"test2\n")?;

    Ok(())
}
40
Shepmaster

引数なしで関数から参照を返す方法はありますか?

いいえ(静的な値への参照を除きますが、ここでは役に立ちません)。

ただし、 OpenOptions::createmainの最初の行を

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

ファイルが存在しない場合は作成され、元の問題が解決されます。

16
fjh

参照はポインターです。関数が実行されると、実行スタックからポップされ、リソースの割り当てが解除されます。

次の例では、xはブロックの最後にドロップされます。その後、参照&xは、いくつかのガベージデータを指します。基本的に、これはぶら下がりポインタです。 Rustコンパイラは安全ではないため、このようなことを許可しません。

fn run() -> &u32 {
    let x: u32 = 42;

    return &x;
} // x is dropped here

fn main() {
    let x = run();
}
0
SnnSnn