如何在不返回的情况下传递 &mut str 并更改原始的 mut str?

How to pass &mut str and change the original mut str without a return?

提问人:rdxdkr 提问时间:7/28/2020 最后编辑:rdxdkr 更新时间:7/28/2020 访问量:9507

问:

我正在从书中学习 Rust,我正在处理第 8 章末尾的练习,但我在将单词转换为猪拉丁语的练习中碰壁了。我特别想看看我是否可以将 a 传递给一个接受 a(也接受切片)的函数并修改其中引用的字符串,以便更改在不需要 a 的情况下反射回外部,就像在 C 中带有 a 一样。&mut String&mut strreturnchar **

我不太确定我是否只是搞砸了语法,或者由于 Rust 的严格规则,它是否比听起来更复杂,我还没有完全掌握。对于里面的终生错误,我记得读过一些解释如何正确处理这种情况的东西,但现在我找不到它,所以如果你也能为我指出来,将不胜感激。to_pig_latin()

另外,您如何看待我处理字符串内部字符和索引的方式?

use std::io::{self, Write};

fn main() {
    let v = vec![
        String::from("kaka"),
        String::from("Apple"),
        String::from("everett"),
        String::from("Robin"),
    ];

    for s in &v {
        // cannot borrow `s` as mutable, as it is not declared as mutable
        // cannot borrow data in a `&` reference as mutable
        to_pig_latin(&mut s);
    }

    for (i, s) in v.iter().enumerate() {
        print!("{}", s);

        if i < v.len() - 1 {
            print!(", ");
        }
    }

    io::stdout().flush().unwrap();
}

fn to_pig_latin(mut s: &mut str) {
    let first = s.chars().nth(0).unwrap();
    let mut pig;

    if "aeiouAEIOU".contains(first) {
        pig = format!("{}-{}", s, "hay");
        s = &mut pig[..]; // `pig` does not live long enough
    } else {
        let mut word = String::new();

        for (i, c) in s.char_indices() {
            if i != 0 {
                word.push(c);
            }
        }

        pig = format!("{}-{}{}", word, first.to_lowercase(), "ay");
        s = &mut pig[..]; // `pig` does not live long enough
    }
}

编辑:这是固定代码,其中包含以下建议。

fn main() {
    // added mut
    let mut v = vec![
        String::from("kaka"),
        String::from("Apple"),
        String::from("everett"),
        String::from("Robin"),
    ];

    // added mut
    for mut s in &mut v {
        to_pig_latin(&mut s);
    }

    for (i, s) in v.iter().enumerate() {
        print!("{}", s);

        if i < v.len() - 1 {
            print!(", ");
        }
    }

    println!();
}

// converted into &mut String
fn to_pig_latin(s: &mut String) {
    let first = s.chars().nth(0).unwrap();

    if "aeiouAEIOU".contains(first) {
        s.push_str("-hay");
    } else {
        // added code to make the new first letter uppercase
        let second = s.chars().nth(1).unwrap();

        *s = format!(
            "{}{}-{}ay",
            second.to_uppercase(),
            // the slice starts at the third char of the string, as if &s[2..]
            &s[first.len_utf8() * 2..],
            first.to_lowercase()
        );
    }
}
字符串 参考 辈子 可变

评论

1赞 E_net4 7/28/2020
代码中存在多个不相关的错误,可以从读取编译器的错误消息开始,并按照编译器的提示修复它们。发生第一个错误是因为向量被声明为不可变(添加 )。那么,推荐阅读:Rust 的 Stringstr 有什么区别?你很可能想要一个这里。其余问题似乎来自分配变量和分配引用后面的值之间的混淆,请参阅:stackoverflow.com/questions/28587698vmut&mut String
0赞 rdxdkr 7/28/2020
@E_net4isaflag 我实际上在一开始就声明了这一点,但我想由于其他错误,编译器给了我一个警告,说可变性不是必需的,所以我没有多想就改变了它。我已经阅读了一些关于 Rust 关于字符串的怪癖的解释,但可能总有改进的余地。最后,我想传递一个包含切片,但似乎这样做我无法避免我想要的喜欢。vmut&mut strreturn

答:

15赞 Masklinn 7/28/2020 #1

我不太确定我是否只是搞砸了语法,或者由于 Rust 的严格规则,它是否比听起来更复杂,我还没有完全掌握。对于 to_pig_latin() 中的终生错误,我记得读过一些解释如何正确处理这种情况的东西,但现在我找不到它,所以如果您也能为我指出来,将不胜感激。

你要做的事情是行不通的:使用可变引用,你可以就地更新裁判,但这在这里非常有限:

  • 不能改变长度或任何与此相关的内容&mut str
  • a 仍然只是一个引用,内存必须存在于某个地方,在这里你在函数中创建新的字符串,然后尝试将它们用作引用的新后备缓冲区,正如编译器告诉你的那样,这是行不通的:字符串将在函数末尾释放&mut str

你可以做的是拿一个 ,它允许你就地修改拥有的字符串本身,这要灵活得多。而且,事实上,它完全对应于您的请求:an 对应于 ,它是指向内存中某个位置的指针。&mut String&mut strchar*

A 也是一个指针,因此 an 是指向内存中某个区域的双指针。String&mut String

所以像这样的东西:

fn to_pig_latin(s: &mut String) {
    let first = s.chars().nth(0).unwrap();
    if "aeiouAEIOU".contains(first) {
        *s = format!("{}-{}", s, "hay");
    } else {
        let mut word = String::new();

        for (i, c) in s.char_indices() {
            if i != 0 {
                word.push(c);
            }
        }

        *s = format!("{}-{}{}", word, first.to_lowercase(), "ay");
    }
}

您还可以通过使用更精细的方法避免一些完整的字符串分配,例如

fn to_pig_latin(s: &mut String) {
    let first = s.chars().nth(0).unwrap();
    if "aeiouAEIOU".contains(first) {
        s.push_str("-hay")
    } else {
        s.replace_range(first.len_utf8().., "");
        write!(s, "-{}ay", first.to_lowercase()).unwrap();
    }
}

虽然 + 不是很可读,而且不太可能有很大的增益,所以那也可能是一个,类似于:replace_rangewrite!format!

fn to_pig_latin(s: &mut String) {
    let first = s.chars().nth(0).unwrap();
    if "aeiouAEIOU".contains(first) {
        s.push_str("-hay")
    } else {
        *s = format!("{}-{}ay", &s[first.len_utf8()..], first.to_lowercase());
    }
}

评论

0赞 rdxdkr 7/28/2020
是的,我没有意识到这会带来这些陷阱。最后一个例子中的 with slice 也非常酷。&mut strformat!