不可以这样做,至少有两个原因可以解释这个问题。
首先,要记住引用是借用的,它们指向某些数据但不拥有它们,它们归属于其他人。在这种情况下,你想要返回的切片字符串是函数拥有的,因为它存储在一个局部变量中。
当函数退出时,所有局部变量都被销毁;这涉及到调用析构函数,而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
}
String
,就可以在技术上将其返回为'static
。 (使用std::mem::forget
),但它将永远保持分配状态。(我猜这就是'static
的意义所在) - Triss Healy&String
和String
之间存在着强烈的语义差异,为了让它们能够协同工作,要么就应该从&String
自动转换为String
,或者&String
和其他引用类型必须以某种方式被传递到堆上,这将需要对类型系统进行一些复杂的改变。因此,在这里不做任何花哨的事情也是最简单的方法。 - Vladimir MatveevCow
。这样可以在可能的情况下使用引用,否则使用拥有所有权的 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()
}
}
String
转换为 &'static str
:fn return_str() -> &'static str {
let string = "ACTG".repeat(10);
Box::leak(string.into_boxed_str())
}
在许多情况下,这是一个非常糟糕的想法,因为每次调用此函数时,内存使用量将永久增长。
如果您想要每次调用返回相同的字符串,请参见:
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
String
转换为 str
。因为一个 String
是在堆上分配的。如果你对它取一个引用,你仍然会使用通过引用访问的在堆上分配的数据。而另一方面,str
存储在可执行文件的数据段中,是静态的。当你对一个字符串取一个引用时,你会得到与常见字符串操作匹配的类型签名,而不是实际的 &str。&str
并通过某些数据结构使用它们。这样做可能会有些麻烦,但肯定可行。#[macro_use]
extern crate lazy_static;
fn return_str<'a>() -> &'a str {
lazy_static! {
static ref STRING: String = {
"ACTG".repeat(10)
};
}
&STRING
}
可以的 - 方法 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
为了达成这个目标,我付出了血、汗和泪水!
String
并将其作为&str
返回。 - LevansString
的理由对我来说毫无意义。在这个“静态”对象中存储一个String
而不是一个&str
。这样更容易,至少与人体工程学相同,从所有权的角度讲更有意义,而且甚至没有任何性能优势。 - user395760