从 Vec<char> 创建一个字符串

Creating a string from Vec<char>

我有一个 Vec<char>,我需要将其转换为 &strString,但我不确定执行此操作的最佳方法。我环顾四周,我发现的所有资源似乎在某种程度上已经过时了。 this question 中的答案似乎不适用于最新版本。

我在 2015 年 3 月 19 日使用 nightly

.collect 的基于迭代器的方法应该可以工作,更新语言更改后:

char_vector.iter().cloned().collect::<String>();

(我选择将 .map(|c| *c) 替换为 .cloned(),但两者都有效。)

如果你的vector可以消耗,你也可以用into_iter来避免clone

fn main() {
    let char_vector = vec!['h', 'e', 'l', 'l', 'o'];
    let str: String = char_vector.into_iter().collect();

    println!("{}", str);
}

您可以将 Vec 转换为 String 而无需进行任何分配。不过它需要相当多的不安全代码:

#![feature(raw, unicode)]
use std::raw::Repr;
use std::slice::from_raw_parts_mut;

fn inplace_to_string(v: Vec<char>) -> String {
    unsafe {
        let mut i = 0;
        {
            let ch_v = &v[..];
            let r = ch_v.repr();
            let p: &mut [u8] = from_raw_parts_mut(r.data as *mut u8, r.len*4);
            for ch in ch_v {
                i += ch.encode_utf8(&mut p[i..i+4]).unwrap();
            }
        }
        let p = v.as_ptr();
        let cap = v.capacity()*4;
        std::mem::forget(v);
        let v = Vec::from_raw_parts(p as *mut u8, i, cap);
        String::from_utf8_unchecked(v)
    }
}

fn main() {
    let char_vector = vec!['h', 'ä', 'l', 'l', 'ö'];
    let str: String = char_vector.iter().cloned().collect();
    let str2 = inplace_to_string(char_vector);

    println!("{}", str);
    println!("{}", str2);
}

PlayPen

详细说明

这会同时创建一个可变 u8 切片和一个 char 切片到同一缓冲区(打破所有 Rust 保证)。请注意 u8 切片是 char 切片的四倍大,因为 char 总是占用 4 个字节。

let ch_v = &v[..];
let r = ch_v.repr();
let v: &mut [u8] = from_raw_parts_mut(r.data as *mut u8, r.len*4);

我们需要它来遍历 unicode 字符并将它们替换为对应的 utf8 编码字符。由于 utf8 总是比 unicode 更短或相同,我们可以保证我们永远不会覆盖任何我们还没有读过的部分。

for ch in ch_v {
    i += ch.encode_utf8(&mut v[i..i+4]).unwrap();
}

因为 char 总是 unicode 并且我们的缓冲区总是恰好是 4 个字节(这是 utf8 编码的 unicode 字符需要的最大字节数),我们可以将我们的字符编码为 utf8 而无需检查是否它有效(它将始终有效)。 encode_utf8 函数 returns utf8 表示的长度。我们的索引 i 是最后写入的 utf8 字符的位置。

最后我们需要做一些清理工作。我们的向量仍然是 Vec<char> 类型。我们得到了我们需要的所有信息(指向堆分配数组和容量的指针)

let p = v.as_ptr();
let cap = v.capacity()*4;

然后我们释放前一个向量的所有义务,如释放内存。

std::mem::forget(v);

最后重新创建长度和容量正确的u8向量,直接转成String。不需要检查到 String 的转换,因为我们已经知道 utf8 是正确的,因为原始 Vec<char> 只能包含正确的 unicode 字符。

let v = Vec::from_raw_parts(p as *mut u8, i, cap);
String::from_utf8_unchecked(v)