如何从函数返回新数据作为参考而没有借阅检查器问题?

Wil*_*iam 3 rust

我正在编写一个函数,该函数接受一个整数并返回该整数乘以2、5的向量。我认为这看起来像:

fn foo(x: &i64) -> Vec<&i64> {
    let mut v = vec![];
    for i in 0..5 {
        let q = x * 2;
        v.push(&q);
    }
    v
}

fn main() {
    let x = 5;
    let q = foo(&x);
    println!("{:?}", q);
}
Run Code Online (Sandbox Code Playgroud)

借阅检查器很麻烦,因为我定义了一个新变量,它在堆栈上分配,并在函数末尾超出范围。

我该怎么办?当然,如果没有编写可以创建新数据的函数,我将一生难忘!我知道有BoxCopy类型的解决方法,但我对惯用的Rust解决方案感兴趣。

我知道我可以返回a,Vec<i64>但是我认为这会遇到相同的问题吗?主要尝试针对一般问题提出一个“集合式”问题:)

B. *_*gel 5

编辑:我只是意识到您写了“我知道有Box,Copy等类型的解决方法,但是我对惯用的防锈解决方案最感兴趣”,但是我已经输入了整个答案。:P下面的解决方案惯用的Rust,这就是内存的工作方式!不要试图用C或C ++返回指向堆栈分配的数据的指针,因为即使编译器没有阻止您,也并不意味着会有好结果。;)


每当您返回引用时,该引用必须已经是该函数的参数。换句话说,如果您要返回对数据的引用,则所有这些数据必须已在函数外部分配。您似乎理解了这一点,我只是想确保清楚。:)

根据您的用例,有许多解决此问题的潜在方法。

在这个特定的示例中,由于您x以后不需要任何东西,因此您可以授予所有权,foo而不必理会引用:

fn foo(x: i64) -> Vec<i64> {
    std::iter::repeat(x * 2).take(5).collect()
}

fn main() {
    let x = 5;
    println!("{:?}", foo(x));
}
Run Code Online (Sandbox Code Playgroud)

但是,假设您不想将所有权转让给foo。只要您不想改变基础值,您仍然可以返回引用向量:

fn foo(x: &i64) -> Vec<&i64> {
    std::iter::repeat(x).take(5).collect()
}

fn main() {
    let x = 5;
    println!("{:?}", foo(&x));
}
Run Code Online (Sandbox Code Playgroud)

...同样,只要您不想派发指向它的新指针,就可以更改基础值:

fn foo(x: &mut i64) -> &mut i64 {
    *x *= 2;
    x
}

fn main() {
    let mut x = 5;
    println!("{:?}", foo(&mut x));
}
Run Code Online (Sandbox Code Playgroud)

...但是,当然,您想两者都做。因此,如果要分配内存并要返回它,则需要在堆栈之外的其他地方进行处理。您可以做的一件事就是使用Box以下命令将其填充到堆中:

// Just for illustration, see the next example for a better approach
fn foo(x: &i64) -> Vec<Box<i64>> {
    std::iter::repeat(Box::new(x * 2)).take(5).collect()
}

fn main() {
    let x = 5;
    println!("{:?}", foo(&x));
}
Run Code Online (Sandbox Code Playgroud)

...尽管有了上述内容,我只是想确保您了解Box使用堆的一般方法。坦白地说,只需使用一种Vec手段即可将您的数据放置在堆上,因此可行:

fn foo(x: &i64) -> Vec<i64> {
    std::iter::repeat(x * 2).take(5).collect()
}

fn main() {
    let x = 5;
    println!("{:?}", foo(&x));
}
Run Code Online (Sandbox Code Playgroud)

上面可能是这里最惯用的示例,尽管您的用例可能会要求有所不同。

此外,也可以由C的剧本拉一招和预分配内存之外foo,然后传递给它的参考:

fn foo(x: &i64, v: &mut [i64; 5]) {
    for i in v {
        *i = x * 2;
    }
}

fn main() {
    let x = 5;
    let mut v = [0; 5];  // fixed-size array on the stack
    foo(&x, &mut v);
    println!("{:?}", v);
}
Run Code Online (Sandbox Code Playgroud)

最后,如果函数必须将引用作为其参数,并且必须对引用的数据进行突变,并且必须复制引用本身,并且必须返回这些复制的引用,则可以使用Cell此功能:

use std::cell::Cell;

fn foo(x: &Cell<i64>) -> Vec<&Cell<i64>> {
    x.set(x.get() * 2);
    std::iter::repeat(x).take(5).collect()
}

fn main() {
    let x = Cell::new(5);
    println!("{:?}", foo(&x));
}
Run Code Online (Sandbox Code Playgroud)

Cell高效和毫不奇怪,不过请注意,它Cell仅对实现Copy特征的类型起作用(所有原始数字类型都可以实现)。如果您的类型未实现,Copy那么您仍然可以使用进行相同的操作RefCell,但是如果您遇到“借用”错误,那么这会带来少量的运行时开销,并在运行时带来恐慌的可能性。