有没有办法返回对函数中创建的变量的引用?

Nex*_*Nex 31 reference lifetime rust

我想编写一个程序,分两步编写一个文件.在程序运行之前,该文件可能不存在.文件名是固定的.

问题是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");
}
Run Code Online (Sandbox Code Playgroud)

She*_*ter 44

fjh绝对正确,但我想更深入地评论一下,并用你的代码触及其他一些错误.


让我们从一个返回引用并查看错误的较小示例开始:

fn try_create<'a>() -> &'a String {
    &String::new()
}
Run Code Online (Sandbox Code Playgroud)

锈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 {
  |               ^^
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

有没有办法从没有参数的函数返回引用?

技术上"是",但对于你想要的,"不".

引用指向现有的内存.在没有参数的函数中,唯一可以引用的是全局常量(具有生命周期&'static)和局部变量.我现在会忽略全局变量.

在像C或C++这样的语言中,您实际上可以引用局部变量并返回它.但是,只要函数返回,就无法保证您引用的内存仍然是您的想法.它可能会保持你期望的一段时间,但最终内存将被重用于其他东西.只要您的代码查看内存并尝试将用户名解释为用户银行帐户中剩余的金额,就会出现问题!

这就是Rust的生命周期所阻止的 - 除了引用值在其当前内存位置有效的时间之外,不允许使用引用.

而不是尝试返回引用,返回拥有的对象.String而不是&str,Vec<T>而不是&[T],T而不是&T

也可以看看:

你的实际问题

查看以下文档OpenOptions::open:

fn open<P: AsRef<Path>>(&self, path: P) -> Result<File>
Run Code Online (Sandbox Code Playgroud)

它返回一个Result<File>,所以我不知道你希望如何返回一个OpenOptions或一个引用.如果你把它重写为:你的功能会起作用:

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

这用于Result::expect恐慌并提供有用的错误消息.当然,对程序内容的恐慌并不是非常有用,因此建议将错误传播出去:

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

OptionResult有很多很好的方法来处理链式错误逻辑.在这里,您可以使用or_else:

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

我还返回Resultmain.所有这些,包括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(())
}
Run Code Online (Sandbox Code Playgroud)

  • 注意:在C++中,返回对堆栈局部变量的引用是Undefined Behavior; 如果它似乎工作,你只是不走运.在常见情况下,编译器应检测问题并发出警告. (4认同)

fjh*_*fjh 16

有没有办法从没有参数的函数返回引用?

否(除了对静态值的引用,但这些对此没有帮助).

但是,您可能想看一下OpenOptions::create.如果您将第一行更改main

let  f = OpenOptions::new().write(true).create(true).open(b"foo.txt");
Run Code Online (Sandbox Code Playgroud)

如果文件尚不存在,将创建该文件,这将解决您的原始问题.


snn*_*snn 9

引用是指针。一旦函数被执行,它们就会从执行堆栈中弹出并且资源被取消分配。

对于以下示例,x在块的末尾删除。在那之后,引用&x将指向一些垃圾数据。基本上它是一个悬空指针。Rust 编译器不允许这样的事情,因为它不安全。

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

    return &x;
} // x is dropped here

fn main() {
    let x = run();
}
Run Code Online (Sandbox Code Playgroud)

  • 像这样的块末尾的“return”并不惯用。 (7认同)
  • 第一个答案不必要地冗长,第二个答案不够详细。选择 return 来强调。 (3认同)
  • 尽管不是惯用的“返回”,我发现这个答案有最清晰的解释。 (3认同)

nav*_*aid 8

这是对snnsnn 的回答的详细说明,它简要地解释了问题,但不太具体。

Rust 不允许返回对在函数中创建的变量的引用。有解决方法吗?是的,只需将该变量放在一个Box 中然后返回它。例子:

fn run() -> Box<u32> {
    let x: u32 = 42;
    return Box::new(x);
} 

fn main() {
    println!("{}", run());
}
Run Code Online (Sandbox Code Playgroud)

Rust 游乐场中的代码

根据经验,为了避免 Rust 中的类似问题,返回一个拥有的对象(Box、Vec、String 等)而不是对变量的引用:

  • Box<T> 代替 &T
  • Vec<T> 代替 &[T]
  • String 代替 &str

对于其他类型,请参阅Rust 类型周期表以找出要使用的拥有对象。

当然,在这个例子中你可以简单地返回值(T而不是&Tor Box<T>

fn run() -> u32 {
    let x: u32 = 42;
    return x;
} 
Run Code Online (Sandbox Code Playgroud)

  • 这个答案是错误的并且具有误导性。为什么要将现成的数据装箱然后返回。它增加了不必要的间接性,从而增加了成本。 (4认同)
  • 即使您对适合装箱的变量进行装箱,该示例仍然是错误的,因为引用用于将外部范围的引用传递到函数中,换句话说,是借用外部变量,以避免接收和的混乱返回。你的例子完全颠倒了这个逻辑。它会让新手感到困惑,并且没有真正的用例。 (2认同)

归档时间:

查看次数:

12957 次

最近记录:

6 年,5 月 前