将本地String作为切片返回(&str)

and*_*man 37 rust

有几个问题似乎与我遇到的问题有关.例如,请看这里这里.基本上我正在尝试String在本地函数中构建一个函数,但是然后将其作为一个函数返回&str.切片不起作用,因为寿命太短.我无法str直接在函数中使用,因为我需要动态构建它.但是,我也不想返回a,String因为一旦它构建完成,它进入的对象的性质就是静态的.有没有办法让我的蛋糕也吃?

这是一个最小的非编译复制:

fn return_str<'a>() -> &'a str {
    let mut string = "".to_string();

    for i in 0..10 {
        string.push_str("ACTG");
    }

    &string[..]
}
Run Code Online (Sandbox Code Playgroud)

Vla*_*eev 61

不,你不能这样做.至少有两种解释为何如此.

首先,请记住引用是借用的,即它们指向某些数据但不拥有它,它由其他人拥有.在这种特殊情况下,字符串(您要返回的切片)由函数拥有,因为它存储在局部变量中.

当函数退出时,它的所有局部变量都被销毁; 这涉及调用析构函数,析构函数String释放字符串使用的内存.但是,您希望返回指向为该字符串分配的数据的借用引用.这意味着返回的引用立即变为悬空 - 它指向无效的内存!

除此之外,还创建了Rust,以防止出现此类问题.因此,在Rust中,不可能返回指向函数局部变量的引用,这在C语言中是可能的.

还有另一种解释,稍微更正式一些.我们来看看你的功能签名:

fn return_str<'a>() -> &'a str
Run Code Online (Sandbox Code Playgroud)

请记住,生命周期和泛型参数都是参数:它们由函数的调用者设置.例如,某些其他函数可能会像这样调用它:

let s: &'static str = return_str();
Run Code Online (Sandbox Code Playgroud)

这要求'a'static,但它当然是不可能的-你的函数不引用返回到静态存储器,它返回一个严格较小一生的参考.因此,这样的函数定义是不合理的,并且被编译器禁止.

无论如何,在这种情况下,你需要返回一个拥有类型的值,在这种特殊情况下,它将是一个拥有的String:

fn return_str() -> String {
    let mut string = String::new();

    for _ in 0..10 {
        string.push_str("ACTG");
    }

    string
}
Run Code Online (Sandbox Code Playgroud)

  • 从技术上讲,只要你泄漏了`String`,你就可以将`String`作为``static`返回.(使用`std :: mem :: forget`),但它将永远保持分配状态.(我猜这是'静态'的重点) (5认同)
  • @Sens所有局部变量都分配在当前执行函数的堆栈帧中。当函数返回时,堆栈帧及其所有内容都会被释放。因此,如果您返回对局部变量之一的引用,则该引用将在函数返回后立即失效。唯一的解决方法是不在堆栈上而是在其他地方分配这样的局部变量,并返回对 _that_ 的引用,但这种方法违背了 Rust 的各种机制(例如,现在不清楚谁拥有这个值)。顺便说一句,其他带有 GC 的语言正是这样做的。 (2认同)

She*_*ter 6

您可以选择泄漏内存以将 a 转换String为 a &'static str

fn return_str() -> &'static str {
    let string = "ACTG".repeat(10);

    Box::leak(string.into_boxed_str())
}
Run Code Online (Sandbox Code Playgroud)

在许多情况下,这是一个非常糟糕的主意,因为每次调用此函数时内存使用量都会永远增长。

如果您想每次调用都返回相同的字符串,请参见:


She*_*ter 5

在某些情况下,您会传递一个字符串切片,并且可能有条件地想要创建一个新字符串。在这些情况下,您可以返回Cow。这将在可能的情况下提供参考,String否则将为拥有:

use std::borrow::Cow;

fn return_str<'a>(name: &'a str) -> Cow<'a, str> {
    if name.is_empty() {
        let name = "ACTG".repeat(10);
        name.into()
    } else {
        name.into()
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 天哪,我开始认为我的用例(有条件地通过字符串引用或根据需要修改字符串)是不可能的,直到我最终偶然发现了这一点。 (4认同)