如何用新格式化的String替换字符串切片的Vec中的一个元素?

问题描述 投票:2回答:2

我试图替换字符串切片列表中的一行,并且无法使其与生命周期正确。

这是我的代码:

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

这必须是借用和生命的东西,但我无法弄清楚它是什么。

rust
2个回答
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>的向量。此数据结构能够存储引用或拥有的字符串。


2
投票

以下是我通过更新它以使用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
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.