可互换地使用str和String

Tim*_*mmm 7 string lifetime rust

假设我正在尝试使用Rust中的一个花哨的零拷贝解析器&str,但有时我需要修改文本(例如,实现变量替换).我真的想做这样的事情:

fn main() {
    let mut v: Vec<&str> = "Hello there $world!".split_whitespace().collect();

    for t in v.iter_mut() {
        if (t.contains("$world")) {
            *t = &t.replace("$world", "Earth");
        }
    }

    println!("{:?}", &v);
}
Run Code Online (Sandbox Code Playgroud)

但当然String归来的t.replace()人活得不够久.这有什么好办法吗?也许有一种类型意味着"理想情况下,&str但如果必要的话String"?或者也许有一种方法可以使用生命周期注释告诉编译器返回String应该保持活动直到结束main()(或具有相同的生命周期v)?

oli*_*obk 9

Rust具有您想要的Cow(Clone On Write)类型.

use std::borrow::Cow;

fn main() {
    let mut v: Vec<_> = "Hello there $world!".split_whitespace()
                                             .map(|s| Cow::Borrowed(s))
                                             .collect();

    for t in v.iter_mut() {
        if t.contains("$world") {
            *t.to_mut() = t.replace("$world", "Earth");
        }
    }

    println!("{:?}", &v);
}
Run Code Online (Sandbox Code Playgroud)

正如@sellibitze正确指出的那样,to_mut()创建一个new String会导致堆分配存储先前借用的值.如果你确定你只是借用了字符串,那么你可以使用

*t = Cow::Owned(t.replace("$world", "Earth"));
Run Code Online (Sandbox Code Playgroud)

如果Vec包含Cow::Owned元素,这仍然会丢弃分配.您可以使用以下非常脆弱和不安全的代码(它确实直接基于字节的UTF-8字符串操作,并且依赖于替换恰好是完全相同的字节数.)在for循环中.

let mut last_pos = 0; // so we don't start at the beginning every time
while let Some(pos) = t[last_pos..].find("$world") {
    let p = pos + last_pos; // find always starts at last_pos
    last_pos = pos + 5;
    unsafe {
        let s = t.to_mut().as_mut_vec(); // operating on Vec is easier
        s.remove(p); // remove $ sign
        for (c, sc) in "Earth".bytes().zip(&mut s[p..]) {
            *sc = c;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,这是根据"$ world" - >"Earth"映射精确定制的.任何其他映射都需要在不安全的代码中仔细考虑.

  • 这里的`to_mut`只会创建一个不必要的`String`值(涉及堆内存分配),它会被立即覆盖(涉及解除分配).我将这一行改为`*t = Cow :: Owned(t.replace("$ world","Earth"));`以避免这种开销. (2认同)

DK.*_*DK. 8

std::borrow::Cow,特别用作Cow<'a, str>,'a被解析的字符串的生命周期.

use std::borrow::Cow;

fn main() {
    let mut v: Vec<Cow<'static, str>> = vec![];
    v.push("oh hai".into());
    v.push(format!("there, {}.", "Mark").into());

    println!("{:?}", v);
}
Run Code Online (Sandbox Code Playgroud)

生产:

["oh hai", "there, Mark."]
Run Code Online (Sandbox Code Playgroud)