我正在尝试替换字符串切片列表中的一行,并且无法使用生命周期正确地替换它。
这是我的代码:
pub struct SomeDataType<'a> {
pub lines: Vec<&'a str>,
// other fields omitted
}
impl<'a> SomeDataType<'a> {
pub fn parse(text: &str) -> Result<SomeDataType, String> {
let lines: Vec<&str> = text.lines().collect();
Ok(SomeDataType { lines })
}
// replace first occurrence, and return original value
pub fn replace_placeholder(&mut self, real_value: &str) -> Option<String> {
let newstr = format!("## {}", real_value);
for line in self.lines.iter_mut() {
if line.starts_with("## PLACEHOLDER") {
let original: String = String::from(*line);
*line = newstr.as_str();
return Some(original);
}
}
None
}
}
fn main() {
let text = r##"
Lorem ipsum
## PLACEHOLDER 1
dolor sit amet,
## PLACEHOLDER 2
consectetur adipiscing elit,
"##;
let mut x = SomeDataType::parse(text).unwrap();
let original = x.replace_placeholder("The Real Value");
println!("ORIGINAL VALUE: {:?}", original); //prints: ORIGINAL VALUE: Some("## PLACEHOLDER 1")
println!("{}", x.lines.join("\n")) //prints the text with first occurrence replaced
}
error[E0597]: `newstr` does not live long enough
--> src/main.rs:18:25
|
6 | impl<'a> SomeDataType<'a> {
| -- lifetime `'a` defined here
...
18 | *line = newstr.as_str();
| --------^^^^^^---------
| | |
| | borrowed value does not live long enough
| assignment requires that `newstr` is borrowed for `'a`
...
23 | }
| - `newstr` dropped here while still borrowed
这一定是借来的,而且寿命很长,但是我不知道是什么。
答案 0 :(得分:5)
您的数据结构
pub struct SomeDataType<'a> {
lines: Vec<&'a str>,
}
存储引用到字符串切片。由于在Rust中引用必须始终有效,因此这些字符串切片的生存期必须比SomeDataType
的实例更长,并且每个字符串切片的生存期必须至少为'a
。
您的函数replace_placeholder()
在此行中创建一个新的本地String
实例:
let newstr = format!("## {}", real_value);
该String
实例仅在函数运行时有效,因为它是局部变量。为了能够在self.lines
中存储对此字符串的引用,它必须至少生存'a
的生存期SomeDataType
,而不是生存。这就是编译器抱怨的原因。
使用当前的数据结构,您实际上无法做到这一点。您在replace_placeholder()
中创建的任何字符串仅在函数运行时有效,除非您可以将字符串的所有权传递给寿命更长的数据结构。 SomeDataType
不能拥有所有权-它仅存储引用。
最简单的解决方案是将数据类型定义更改为
pub struct SomeDataType {
lines: Vec<String>,
}
因此它拥有所有字符串。这将需要您从解析的行中创建新的String
对象,因此您将复制所有行。这不太可能成为问题,但是如果出于某种原因需要避免这种开销,则也可以使用向量Cow<'a, str>
。这种数据结构能够存储引用或拥有的字符串。
答案 1 :(得分:2)
这是我如何按照Sven Marnach和Stargateur建议使用Cow
对其进行更新来使代码工作的方式:
use std::borrow::Cow;
pub struct SomeDataType<'a> {
pub lines: Vec<Cow<'a, str>>,
// other fields omitted
}
impl<'a> SomeDataType<'a> {
pub fn parse(text: &str) -> Result<SomeDataType, String> {
let lines = text.lines().map(Cow::Borrowed).collect();
Ok(SomeDataType { lines })
}
// replace first occurrence, and return original
pub fn replace_placeholder(&mut self, real_value: &str) -> Option<String> {
let newstr = Cow::Owned(format!("## {}", real_value));
for line in self.lines.iter_mut() {
if line.starts_with("## PLACEHOLDER") {
let original: String = String::from(line.clone());
*line = newstr;
return Some(original);
}
}
None
}
}
或者,使用String
看起来更简单,而且可能也更优雅:
pub struct SomeDataType {
pub lines: Vec<String>,
// other fields omitted
}
impl SomeDataType {
pub fn parse(text: &str) -> Result<SomeDataType, String> {
let lines = text.lines().map(String::from).collect();
Ok(SomeDataType { lines })
}
// replace first occurrence, and return original
pub fn replace_placeholder(&mut self, real_value: &str) -> Option<String> {
let newstr = format!("## {}", real_value);
for line in self.lines.iter_mut() {
if line.starts_with("## PLACEHOLDER") {
let original = line.clone();
*line = newstr;
return Some(original);
}
}
None
}
}