如何将str改组到位

Mel*_*lle 4 rust

我想在Rust中使用一个字符串,但我似乎错过了一些东西.修复可能是微不足道的......

use std::rand::{Rng, thread_rng};

fn main() {
    // I want to shuffle this string...
    let mut value: String = "SomeValue".to_string();
    let mut bytes = value.as_bytes();
    let mut slice: &mut [u8] = bytes.as_mut_slice();

    thread_rng().shuffle(slice);

    println!("{}", value); 
}
Run Code Online (Sandbox Code Playgroud)

我得到的错误是

<anon>:8:36: 8:41 error: cannot borrow immutable dereference of `&`-pointer `*bytes` as mutable
<anon>:8         let mut slice: &mut [u8] = bytes.as_mut_slice();
                                            ^~~~~
Run Code Online (Sandbox Code Playgroud)

我读到了关于String :: as_mut_vec()但它不安全,所以我宁愿不使用它.

huo*_*uon 12

还有要做到这一点,部分原因是由于字符串的UTF-8编码的性质也没有很好的办法,部分是由于Unicode和文本的内在属性.

至少有三层可以在UTF-8字符串中混洗的东西:

  • 原始字节
  • 编码的码点
  • 字形

洗牌原始字节很可能会给一个无效的UTF-8字符串作为输出,除非该字符串是完全ASCII.非ASCII字符被编码为多个字节的特殊序列,并且这些洗牌将almostly肯定不会在年底以正确的顺序得到他们.因此,改组字节通常不好.

改组代码点(char在Rust中)更有意义,但仍然存在"特殊序列"的概念,其中所谓的组合字符可以分层到单个字母上添加变音符号等(例如字母ä可以写为a加U + 0308,代表分音符的代码点).因此,随机字符不会产生无效的UTF-8字符串,但它可能会破坏这些代码点序列并提供无意义的输出.

这让我想到了字形:构成单个可见字符的代码点序列(例如,ä当写为一个或两个代码点时仍然是单个字形).这将给出最可靠明智的答案.

然后,一旦你决定要改组哪个洗牌策略就可以:

  • 如果字符串保证纯粹是ASCII,那么使用字符进行混洗.shuffle是合理的(使用ASCII假设,这相当于其他字符串)
  • 否则,没有标准的就地操作方法,可以将元素作为迭代器(.chars()对于代码点或.graphemes(true)字形),将它们放入一个向量中.collect::<Vec<_>>(),将向量混合,然后将所有内容收集回新的内容String,例如.iter().map(|x| *x).collect::<String>().

处理代码点和字形的难度是因为UTF-8不将它们编码为固定宽度,因此无法将随机码点/字形输出并将其插入其他地方,或以其他方式有效地交换两个元素...而不仅仅是解码一切都变成了外在的Vec.

不合适是不幸的,但字符串很难.

(如果你的字符串保证是ASCII,那么在类型级别使用类似Ascii提供的类型ascii将是保持正确的好方法.)


作为三件事差异的一个例子,看看:

fn main() {
    let s = "U???????n?????i??c????o???de????";
    println!("bytes: {}", s.bytes().count());
    println!("chars: {}", s.chars().count());
    println!("graphemes: {}", s.graphemes(true).count());
}
Run Code Online (Sandbox Code Playgroud)

它打印:

bytes: 57
chars: 32
graphemes: 7
Run Code Online (Sandbox Code Playgroud)

(生成您自己的,它演示了将多个组合字符放在一个字母上.)