«How do I convert between String, &str, Vec<u8> and &[u8]?» — your checklist for optimization in Rust
\n\nIf you've ever written Rust code that works with text or bytes, you've likely encountered the question: «How do I convert String to &str? And Vec<u8> to &[u8]? And back?». This is one of the most popular questions on Stack Overflow (tens of thousands of views), and for good reason. Seemingly simple conversions hide pitfalls, especially when it comes to performance.
In this article, we won't just list the «how to» methods. We'll break down why some methods are faster than others, and when to use each one. You'll learn how to avoid unnecessary copies, extra allocations, and how to write truly efficient Rust code.
\n\nWhy know this? Context and performance
\nRust is a language that gives you control over memory. But with control comes responsibility. Converting between strings and bytes is one of the most common operations. For example, when working with network protocols, data serialization (JSON, Protobuf), or simply reading files. Choosing the wrong conversion can lead to copying data that could have been used without copying. In the world of systems programming, this is critical.
\n\nLet's break down the four main types and learn to move between them without loss.
\n\nString and &str: two sides of the same coin
\nString is an owned, mutable, UTF-8 string. &str is a borrowed, immutable string slice. The main rule: String owns the data, &str is a reference to the data.
How to get &str from String (and back)
\nThis is the simplest and safest case.
\n\nlet my_string = String::from("Hello, world!");\n// 1. Get &str from String (just borrow)\nlet my_str: &str = &my_string;\n// Or using as_str()\nlet my_str2: &str = my_string.as_str();\n\n// 2. Get String from &str (create a new copy)\nlet new_string: String = my_str.to_string();\n// Or\nlet new_string2: String = String::from(my_str);\n\nImportant: Converting &str to String always creates a new memory allocation. This is expensive. Converting String to &str is just taking a reference (free).
How to get Vec<u8> from String (and vice versa)
\nThis is where it gets interesting, because internally String is just a Vec<u8> with a guarantee of valid UTF-8.
let my_string = String::from("Rust");\n\n// 3. String -> Vec<u8>\n// Option A (copying): safe, but expensive\nlet bytes_vec_copy: Vec<u8> = my_string.as_bytes().to_vec();\n\n// Option B (no copying, consumes String):\nlet bytes_vec_owned: Vec<u8> = my_string.into_bytes(); // my_string no longer exists!\n\n// 4. Vec<u8> -> String (check for valid UTF-8)\nlet new_string: String = String::from_utf8(bytes_vec_owned).expect("Invalid UTF-8");\n// If you are sure it's valid:\nlet new_string_unchecked: String = unsafe { String::from_utf8_unchecked(bytes_vec_copy) };\n\nKey point: into_bytes() is a zero-cost conversion. It takes the internal Vec<u8> buffer and gives it to you without copying a single byte. as_bytes().to_vec() is an explicit copy.
The from_utf8_unchecked method is unsafe. Use it only if you are 100% sure the bytes are valid UTF-8. Otherwise, the program may panic or, worse, exhibit UB (undefined behavior).