有几个问题似乎与我遇到的问题有关。例如,请参阅here和here。基本上我尝试在本地函数中构建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[..]
}
答案 0 :(得分:58)
不,你不能这样做。至少有两种解释为何如此。
首先,请记住引用是借用的,即它们指向某些数据但不拥有它,它由其他人拥有。在这种特殊情况下,字符串(您要返回的切片)由函数拥有,因为它存储在局部变量中。
当函数退出时,它的所有局部变量都被销毁;这涉及调用析构函数,String
的析构函数释放字符串使用的内存。但是,您希望返回指向为该字符串分配的数据的借用引用。这意味着返回的引用立即变为悬空 - 它指向无效的内存!
还有另一种解释,稍微更正式一些。我们来看看你的函数签名:
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
为此花了很多汗水和眼泪!