我试图替换字符串切片列表中的一行,并且无法使其与生命周期正确。
这是我的代码:
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
这必须是借用和生命的东西,但我无法弄清楚它是什么。
您的数据结构
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>
的向量。此数据结构能够存储引用或拥有的字符串。
以下是我通过更新它以使用Cow
作为Sven Marnach和Stargateur推荐的代码的工作原理:
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
}
}