使用函数编辑字符串

时间:2014-12-05 14:28:09

标签: rust lifetime

我试图通过将其传递给mutate()来编辑一个字符串,见下文。

简化示例:

fn mutate(string: &mut &str) -> &str {
    string[0] = 'a'; // mutate string
    string
}

fn do_something(string: &str) {
    println!("{}", string);
}

fn main() {
    let string = "Hello, world!";
    loop {
        string = mutate(&mut string);
        do_something(string);
    }
}

但是我得到以下编译错误:

main.rs:1:33: 1:37 error: missing lifetime specifier [E0106]
main.rs:1 fn mutate(string: &mut &str) -> &str {
                                          ^~~~
main.rs:1:33: 1:37 help: this function's return type contains a borrowed value, but the signature does not say which one of `string`'s 2 elided lifetimes it is borrowed from
main.rs:1 fn mutate(string: &mut &str) -> &str {
                                          ^~~~

为什么我会收到此错误,如何实现我的目标?

1 个答案:

答案 0 :(得分:10)

您根本无法更改字符串切片。 &mut &str无论如何都不是合适的类型,因为它实际上是指向不可变切片的可变指针。所有字符串切片都是不可变的。

Rust字符串是有效的UTF-8序列,UTF-8是可变宽度编码。因此,通常更改字符可能会以字节为单位更改字符串的长度。这不能用切片完成(因为它们总是有固定的长度)并且它可能导致重新分配所拥有的字符串。此外,在99%的情况下,更改字符串中的字符并不是您真正想要的。

为了使用unicode代码点做你想做的事,你需要做这样的事情:

fn replace_char_at(s: &str, idx: uint, c: char) -> String {
    let mut r = String::with_capacity(s.len());
    for (i, d) in s.char_indices() {
        r.push(if i == idx { c } else { d });
    }
    r
}

然而,这具有O(n)效率,因为它必须遍历原始切片,并且它也无法正确处理复杂字符 - 它可能会替换字母但会留下重音,反之亦然。

更正确的文本处理方式是迭代字形集群,它将正确地采用变音符号和其他类似的东西(主要是):

fn replace_grapheme_at(s: &str, idx: uint, c: &str) -> String {
    let mut r = String::with_capacity(s.len());
    for (i, g) in s.grapheme_indices(true) {
        r.push_str(if i == idx { c } else { g });
    }
    r
}

std::ascii模块中对纯ASCII字符串也有一些支持,但很可能很快就会进行改革。无论如何,这就是它的使用方式:

fn replace_ascii_char_at(s: String, idx: uint, c: char) -> String {
    let mut ascii_s = s.into_ascii();
    ascii_s[idx] = c.to_ascii();
    String::from_utf8(ascii_s.into_bytes()).unwrap()
}

如果s包含非ASCII字符或c不是ASCII字符,则会发生混乱。