rust 以切片形式返回本地字符串(&str)

juzqafwq  于 2022-11-24  发布在  其他
关注(0)|答案(7)|浏览(188)

有几个问题似乎和我遇到的问题是一样的。例如,参见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[..]
}
tmb3ates

tmb3ates1#

不,你不能这么做。至少有两种解释。
首先,请记住引用是借用的,也就是说,它们指向一些数据,但并不拥有它,它是由其他人拥有的。在这个特定的例子中,字符串,一个你想返回的切片,是由函数拥有的,因为它存储在一个局部变量中。
当函数退出时,它的所有局部变量都被销毁;这涉及到调用析构函数,String的析构函数释放字符串使用的内存。然而,您希望返回一个借用的引用,指向为该字符串分配的数据。这意味着返回的引用立即成为悬空的-它指向无效的内存!
Rust就是为了防止这样的问题而创建的,因此,在Rust中不可能返回指向函数局部变量的引用,而在C语言中是可能的。
还有另一种解释,稍微正式一点。让我们看看你的函数签名:

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

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

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
}
uemypmqf

uemypmqf2#

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

lymnna713#

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

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

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

在许多情况下,这是一个 * 非常糟糕的主意 *,因为每次调用此函数时,内存使用量都会永远增长。
如果希望每次调用都返回相同的字符串,请参见:

mqkwyuun

mqkwyuun4#

问题是您试图创建一个字符串引用,而该字符串在函数返回时将消失。
在这种情况下,一个简单的解决方案是将空字符串传递给函数,这将显式地确保引用的字符串仍然存在于函数返回的作用域中:

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

d4so4syb

d4so4syb5#

  • 如果可以在编译时以静态方式创建结果 * STRING *,则这将是一个没有内存泄漏的解决方案 *
#[macro_use]
extern crate lazy_static;
    
fn return_str<'a>() -> &'a str {
    lazy_static! {
        static ref STRING: String = {
            "ACTG".repeat(10)
        };
    }

    &STRING
}
7bsow1i6

7bsow1i66#

这是一个古老的问题,但也是一个非常常见的问题。有很多答案,但没有一个能解决人们对弦和弦片的明显误解,这种误解源于不知道它们的真实性质。
但是,在解决隐含的问题之前,让我们先从显而易见的问题开始:我们可以返回对局部变量的引用吗?
我们要求实现的是悬空指针的教科书定义。当函数完成其执行时,局部变量将被丢弃。换句话说,它们将从执行堆栈中弹出,并且此后对局部变量的任何引用都将指向一些垃圾数据。
最好的做法是返回字符串或它的克隆。没有必要纠缠于速度。
但是,我认为问题的实质是,是否有办法将String转换为str?答案是否定的,这就是误解所在:
你不能通过借用String来把它变成str。因为String是堆分配的。如果你引用它,你仍然使用堆分配的数据,但是通过引用。另一方面,str直接存储在可执行文件的数据段中,并且是静态的。当你引用一个字符串时,您将获得常见字符串操作匹配类型签名,而不是实际的&字符串。
您可以查看此帖子以了解详细解释:
Rust's 'String'和'str'有什么区别?
现在,如果您完全使用静态文本,则可能有一种解决方法来解决此特定用例:
既然你使用了A、C、G、T四个碱基的组合,那么你可以用&str的形式列出所有可能的结果,并通过某种数据结构来使用它们。

hgc7kmma

hgc7kmma7#

是的,您可以-方法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

这是用了多少心血和汗水才做到的!

相关问题