Как преобразовать вектор байтов (Vec<u8>) в строку (String) в Rust?
Основной способ: String::from_utf8()
Для преобразования Vec<u8> в String в Rust используется функция String::from_utf8(). Она проверяет, что все байты являются валидной UTF-8 последовательностью, и возвращает Result<String, FromUtf8Error>. Вот как это работает с вашим примером:
fn main() {
let bytes: Vec<u8> = vec![72, 101, 108, 108, 111]; // "Hello"
match String::from_utf8(bytes) {
Ok(text) => println!("Успех: {}", text),
Err(e) => eprintln!("Ошибка UTF-8: {}", e),
}
}
Обработка ошибок и альтернативы
Если вы уверены, что данные всегда валидны, можно использовать .unwrap() или .expect(), но в production-коде лучше избегать паники:
// Только если 100% уверены в данных:
let text = String::from_utf8(bytes).expect("Данные не являются UTF-8");
Если вам нужно сохранить невалидные байты, используйте String::from_utf8_lossy(). Она заменит невалидные последовательности на символ замены � (U+FFFD) и вернёт Cow<str>:
let bytes: Vec<u8> = vec![72, 101, 108, 108, 111, 0xFF]; // 0xFF — невалидный байт
let text = String::from_utf8_lossy(&bytes); // "Hello�"
println!("{}", text); // Работает без паники
Разница между Vec<u8> и &[u8]
Важное различие:
String::from_utf8()принимает владениеVec<u8>— он потребляет вектор, и вы не сможете использовать его после преобразования.String::from_utf8_lossy()принимает ссылку&[u8]— данные не копируются, но возвращаетсяCow<str>, который может быть заимствованным или владеющим.
Пример со срезом:
let bytes: Vec<u8> = vec![72, 101, 108, 108, 111];
let slice: &[u8] = &bytes;
let text = String::from_utf8_lossy(slice);
// bytes всё ещё доступен!
println!("bytes: {:?}", bytes);
Советы по производительности
- Если данные гарантированно валидны, используйте
unsafe { String::from_utf8_unchecked(bytes) }— это убирает проверку UTF-8, но может привести к UB при невалидных данных. - Для больших объёмов данных
from_utf8_lossyможет быть быстрее, так как не копирует данные, если они валидны (благодаряCow).
Помните: в Rust строки всегда должны быть валидным UTF-8. Это гарантирует безопасность и предсказуемость работы с текстом. Всегда обрабатывайте ошибки кодировки, если источник данных ненадёжен!
<<Альтернативный подход: str::from_utf8() для срезов
Если вам не нужно владение строкой, а достаточно временного доступа к тексту, используйте std::str::from_utf8(). Она работает с &[u8] и возвращает Result<&str, Utf8Error> — без выделения памяти и копирования данных:
let bytes: Vec<u8> = vec![72, 101, 108, 108, 111];
match std::str::from_utf8(&bytes) {
Ok(text) => println!("Срез строки: {}", text),
Err(e) => eprintln!("Ошибка: {}", e),
}
Когда это полезно
- Парсинг данных: если вам нужно только прочитать строку без сохранения.
- Снижение аллокаций: в высоконагруженных системах, где важна производительность.
- Работа с большими данными: избегайте копирования, если строка не будет изменяться.
Если в итоге нужна String, просто вызовите .to_string() на полученном &str — но это уже создаст копию. Для прямого преобразования с владением используйте String::from_utf8() из первого ответа.