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

时间:2015-04-03 07:42:18

标签: rust

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

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

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

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

    &string[..]
}

6 个答案:

答案 0 :(得分:58)

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

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

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

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

还有另一种解释,稍微更正式一些。我们来看看你的函数签名:

fn return_str<'a>() -> &'a str

请记住,生命周期和通用参数都是参数:它们由函数的调用者设置。例如,其他一些函数可能会这样称呼它:

let s: &'static str = return_str();

这需要'a'static,但它当然是不可能的 - 您的函数不返回对静态内存的引用,它返回一个严格较小生命周期的引用。因此,这样的函数定义是不合理的,并且被编译器禁止。

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

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

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

    string
}

答案 1 :(得分:4)

在某些情况下,会为您传递一个字符串切片,并且可能有条件地要创建一个新字符串。在这些情况下,您可以返回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()
    }
}

答案 2 :(得分:2)

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

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

    Box::leak(string.into_boxed_str())
}

在很多情况下,这是一个真正的坏主意,因为每次调用此函数时,内存使用量将永远增长。

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

答案 3 :(得分:0)

问题是您试图创建对字符串的引用,该字符串将在函数返回时消失。

在这种情况下,一个简单的解决方案是将空字符串传递给函数。这将明确确保所引用的字符串在函数返回的范围内仍然存在:

fn return_str(s: &mut String) -> &str {

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

    &s[..]
}

fn main() {
    let mut s = String::new();
    let s = return_str(&mut s);
    assert_eq!("ACTGACTGACTGACTGACTGACTGACTGACTGACTGACTG", s);
}

Rust Playground中的代码: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=2499ded42d3ee92d6023161fe82e9b5f

答案 4 :(得分:0)

如果可以在编译时以静态方式创建结果 STRING ,这将是一个没有内存泄漏的解决方案

#[macro_use]
extern crate lazy_static;
    
fn return_str<'a>() -> &'a str {
    lazy_static! {
        static ref STRING: String = {
            "ACTG".repeat(10)
        };
    }

    &STRING
}

答案 5 :(得分:-1)

是的-方法replace_range提供了解决方法-

let a = "0123456789";
//println!("{}",a[3..5]);  fails - doesn't have a size known at compile-time
let mut b = String::from(a);
b.replace_range(5..,"");
b.replace_range(0..2,"");
println!("{}",b); //succeeds 

为此花了很多汗水和眼泪!